Dynamic deep setting for a JavaScript object [duplicate]

Given a string for a object property path, how do I set this property dynamically.

Given this sample object:

var obj = {
    a: {
        b: [ { c: 'Before' } ]
    }
};

It should be able to set the value with a helper function like this:

setToValue(obj, 'After', 'a.b.0.c');

I tried it with the following code. But parent is a copy if the variable not a reference.

function setToValue(obj, value, path) {
    var arrPath = path.split('.'),
        parent = obj;

    for (var i = 0, max = arrPath.length; i < max; i++) {
        parent = parent[arrPath[i]];
    }

    parent = value;
}

a) What's wrong with a simple a.b[0].c = 'After'?

As for the method:

function setToValue(obj, value, path) {
    var i;
    path = path.split('.');
    for (i = 0; i < path.length - 1; i++)
        obj = obj[path[i]];

    obj[path[i]] = value;
}

Here the JSFiddle: http://jsfiddle.net/QycBz/24/


Here is a full solution.

Also creates objects if they don't exist.

function setValue(obj, path, value) {
  var a = path.split('.')
  var o = obj
  while (a.length - 1) {
    var n = a.shift()
    if (!(n in o)) o[n] = {}
    o = o[n]
  }
  o[a[0]] = value
}

function getValue(obj, path) {
  path = path.replace(/\[(\w+)\]/g, '.$1')
  path = path.replace(/^\./, '')
  var a = path.split('.')
  var o = obj
  while (a.length) {
    var n = a.shift()
    if (!(n in o)) return
    o = o[n]
  }
  return o
}

FWIW, those of you wishing to the same in CoffeeScript might find these methods handy - it's a quite straight port of the above code. As an extra bonus, they make sure all the objects in the path exists (the getPropertyByPath doesn't throw exceptions if they don't, and the set method will create empty objects if any objects in the path happen to be null).

getPropertyByPath: (obj, path) ->
  path = path.split('.')
  parent = obj

  if path.length > 1
    parent = parent[path[i]] for i in [0..path.length - 2]

  parent?[path[path.length - 1]]

setPropertyByPath: (obj, path, value) ->
  path = path.split('.')
  parent = obj

  if path.length > 1
    parent = (parent[path[i]] ||= {}) for i in [0..path.length - 2]

  parent[path[path.length - 1]] = value