9

Im trying to figure out if its possible to update a JavaScript object, using a string as the path.

In the example below, I'm trying to figure out how I can update the first books price using store>book>0>price as my path.

I know I can access this by writing data['store']['book'][0]['price'] but I need to be able to do this dynamically. Ive tried a few things but had no luck. Any Ideas?

This needs to work for any depth , not a fixed depth


Data:

 var data = { 
      "store": {
        "book": [ 
          { "category": "reference",
            "author": "Nigel Rees",
            "title": "Sayings of the Century",
            "price": 8.95
          },
          { "category": "fiction",
            "author": "Evelyn Waugh",
            "title": "Sword of Honour",
            "price": 12.99
          }
        ],
        "bicycle": {
          "color": "red",
          "price": 19.95
        }
      }
    }
var path = "store>book>0>price"

Function:

function updateObject(object, path, data) {
    var pathArray = path.split(">");
    // Some code here
} 
updateObject(data, path, "10.00");

Update

As felix pointed out the answer can be found here. Dynamic deep setting for a JavaScript object

Here is a working example for my scenario http://jsfiddle.net/blowsie/Sq8j3/9/

Community
  • 1
  • 1
Blowsie
  • 40,239
  • 15
  • 88
  • 108
  • @mplungjan: Not necessarily. It works with a `for` loop as well. Have a look at the linked question. – Felix Kling Feb 26 '13 at 15:34
  • While the related question is very similar, it does not address the problem of updating the object – Blowsie Feb 26 '13 at 15:41
  • @Blowsie: You mean like the problem of assigning a value to a property? If not, what do you mean? – the system Feb 26 '13 at 15:43
  • 1
    You are right. Those two do: http://stackoverflow.com/q/13719593/218196, http://stackoverflow.com/q/6842795/218196. – Felix Kling Feb 26 '13 at 15:44
  • You deleted your comment, but still: Arrays are just objects, you can access both the same way, so it does not make a difference. Here is a fiddle, [applying this answer](http://stackoverflow.com/a/6842900/218196) to your data: http://jsfiddle.net/Sq8j3/6/. – Felix Kling Feb 26 '13 at 15:54
  • 1
    Why the downvote? I thought this was a good question. I've upvoted it but that it only brings it back to 0... – guypursey Feb 26 '13 at 16:13
  • @guypursey thanks, I don't know why it was downvoted, okay the question was already on SO, but not with the words I searched on – Blowsie Feb 26 '13 at 16:16

3 Answers3

20
function updateObject(object, newValue, path){

  var stack = path.split('>');

  while(stack.length>1){
    object = object[stack.shift()];
  }

  object[stack.shift()] = newValue;

}
DanC
  • 8,595
  • 9
  • 42
  • 64
  • works a charm thankyou http://jsfiddle.net/blowsie/Sq8j3/11/ – Blowsie Feb 26 '13 at 15:56
  • neat Like the duplicate (and my rewrite of it) but without testing for object existence. Still if data is known to work, a very compact version – mplungjan Feb 26 '13 at 15:56
  • very nice, good solution. – Kyle C Oct 15 '15 at 19:57
  • Fantastic solution. I have been wondering how to do this. – mjwrazor Nov 15 '16 at 17:12
  • This is the best I have seen yet. I have made extensions for get, set and add as well: https://embed.plnkr.co/kwF83lMQtKb6Vb8aP8jt/ – unitario Feb 08 '17 at 18:00
  • Is there any easy way to have this function create the path if it doesn't exist? – PHELMS Mar 19 '17 at 08:14
  • I found it helpful to make a copy of the path, `[ ...path ]`, to make sure I didn't accidentally change data - I'd think you'd usually want to avoid mutating that kind of data (as the the `shift()`s will) – skwidbreth Sep 25 '20 at 21:15
3

You want to update your method signature to accept the: object you're modifying, the path string, and the value you're assigning to the final path property.

function updateObject(data, path, value) {
        var pathArray = path.split(">");
        var pointer = data; // points to the current nested object
        for (var i = 0, len = pathArray.length; i < len; i++) {
            var path = pathArray[i];
            if (pointer.hasOwnProperty(path)) {
                if (i === len - 1) { // terminating condition
                    pointer[path] = value;
                } else {
                    pointer = pointer[path];
                }
            } else {
                // throw error or terminate. The path is incorrect
            }
        }
    }

Or recurse. Or use a while loop. But this is the general idea.

Fiddle: http://jsfiddle.net/Sq8j3/8/

Community
  • 1
  • 1
stinkycheeseman
  • 43,437
  • 7
  • 30
  • 49
1

It's slightly confusing that you've called your object data but that data is also an argument of your function. I've changed the argument's name therefore to newVal in order to clear up this potential problem.

This loops through the path and constantly resets a variable called e which starts by pointing to the data object generally and gets more specific as we loop. At the end, you should have an almost reference to the exact property -- we use the last part of the path to set the new value.

function updateObject(newVal, path) {
    var pathArray = path.split(">"),
        i = 0,
        p = pathArray.length - 1, // one short of the full path
        e = data; // "import" object for changing (i.e., create local ref to it)
    for (i; i < p; i += 1) { // loop through path
        if (e.hasOwnProperty(pathArray[i])) { // check property exists
            e = e[pathArray[i]]; // update e reference point
        }
    }
    e[pathArray[i]] = newVal; // change the property at the location specified by path to the new value
};

You might need to add something to catch errors. I have put a check in with the hasOwnProperty() call but you might need something more elaborate than this.

UPDATE

Had made a silly mistake in the code before but it should be working now. As evidenced here.

guypursey
  • 3,114
  • 3
  • 24
  • 42
  • thanks for your efforts, doesn't quite work for me http://jsfiddle.net/blowsie/Sq8j3/2/ – Blowsie Feb 26 '13 at 15:49
  • Thanks for acknowledging the efforts :-) I had made a couple of mistakes in my code which I've now corrected. Unfortunately, this still isn't quite right but you can see something is starting to happen... http://jsfiddle.net/Sq8j3/10/ – guypursey Feb 26 '13 at 15:54
  • @Blowsie Fixed now. I see you have a correct (and more concise) answer now anyway but I didn't like the idea of leaving erroneous code up ! :-) – guypursey Feb 26 '13 at 16:10