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
  • 39,189
  • 15
  • 83
  • 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
  • @FelixKling thankyou very useful – Blowsie Feb 26 '13 at 16:01
  • 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

4 Answers4

20
function updateObject(object, newValue, path){

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

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

  object[stack.shift()] = newValue;

}
DanC
  • 8,305
  • 9
  • 41
  • 64
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
  • 39,309
  • 7
  • 28
  • 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,004
  • 3
  • 21
  • 40
  • 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
0

Refactoring Accessing nested JavaScript objects with string key

I get

var path = "store>book>0>price"
Object.byString = function(o, s) {
    var a = s.split('>');
    while (a.length) {
        var n = a.shift();
        if (n in o) {
            o = o[n];
        } else {
            return;
        }
    }
    return o;
}


function updateObject(data, path) {
  object2update= Object.byString(data, path);
} 
Community
  • 1
  • 1
mplungjan
  • 155,085
  • 27
  • 166
  • 222