1

say I have an object like this:

a : {
  a1 : {
    a2: true
  } 
}

and I have all the path saved in an array:

[a1, a2]

If I want to assign value to a["a1"]["a2"], it is easy:

a["a1"]["a2"] = true;

However when I have a 3 level path like this:

[a1, a2, a3]

I have to manually write the code like this:

a["a1"]["a2"]["a3"] = true;

Is there a way to automatically handle any level of paths so that I don't have to make it explicit for every single case?

Note that "a" can be quite complex so I only want to assign value to this specific element and without touching the rest.

3
  • 1
    I don't understand how the "path saved in an array" has to do with your question. Also, can you just type a.a1.a2.a3? Perhaps edit your question so you have a full function showing what you want to do, then a comment saying which line you think is too verbose that you want to minimize or change. Commented May 14, 2013 at 0:46
  • possible duplicate of Accessing nested JavaScript objects with string key Commented May 14, 2013 at 0:56
  • related: stackoverflow.com/a/10934946/989121 Commented May 14, 2013 at 17:08

5 Answers 5

5

You could iteratively traverse the object with the path like so:

function setDeepProperty(obj, path, value)
{
    var curr = obj;

    for (var depth = 0; depth < path.length - 1; depth++)
    {
        curr = curr[path[depth]];
    }

    curr[path[path.length - 1]] = value;
}

This assumes that the path is valid. Ensure that path[depth] in curr if necessary. The last step in the traversal is done outside of the loops because it would be setting curr to a primitive type instead of referencing an array (as we desire) meaning it wouldn't change the original. Then, as per your example:

var arr = {a1: {a2: { a3: false }}};
setDeepProperty(arr, ["a1", "a2", "a3"], true);

Note here that the nodes in the path are strings.

Sign up to request clarification or add additional context in comments.

Comments

3

There are several ways you could access the properties:

Use a loop:

var obj = {
        a1 : {
            a2: { a3: 'test' }
        } 
    },
    i = 0,
    keyPath = ['a1', 'a2', 'a3'],
    len = keyPath.length;

    for (; i < len; i++) {
        obj = obj[keyPath[i]];
    }

    console.log(obj);

With eval (I don't recommend this however):

var obj = {
            a1 : {
                a2: { a3: 'test' }
            } 
        };

var value = eval('obj.' + keyPath.join('.'));

console.log(value);

You could use the same approach to set a property at a specific key path:

function setProperty(obj, keyPath, value) {
    var i = 0,
        len = keyPath.length - 1;

    for (; i < len; i++) {
        obj = obj[keyPath[i]];
    }

    obj[keyPath[i]] = value;
}

3 Comments

@Bergi: Eh. You weren't gonna do it. :)
@Bergi, Just showing existing alternatives for educationnal purposes.
All are great answers, thank you! Select this answer for being the most comprehensive one.
1

All are elegant solutions, my 2 cents with recursion:-

Test Here

var a = {
    a1: {
        a2: {
            a3: false
        }
    }
};

var path = ['a1', 'a2', 'a3'];

var valueToSet = true;
setValue(0, a);


function setValue(level, ob) {
  var prop = path[level];

  if (!ob.hasOwnProperty(prop)) {
    return;
  }
   if (level == (path.length - 1)) {
    ob[prop] = valueToSet;
    return;
   }

   return setValue(level + 1, ob[prop]);

}
console.log(a);

Comments

0

You have 2 possibilities:

  • the dreaded eval(). I refuse giving code for that
  • an in-out loop:

Code:

var a={
  a1 : {
    a2 : {
      a3: false
    } 
  } 
};
var idx=["a1", "a2", "a3"];

function recReplace(o, i, v) {
  var ii=i.shift();
  if (i.length==0)
    o[ii]=v;
  else
   o[ii]=recReplace(o[ii],i,v);
  return o;
}

b=recReplace(a,idx,true); //or: a=recReplace(a,idx,true);

Comments

0

Sure, it's a simple loop:

var a = {a1:{a2:{}}};

var path = ["a1", "a2", "a3"];
for (var o=a, i=0; i<path.length-1; i++)
    o = o[path[i]]; // notice that this will throw exception
                    // when the objects do not exist already
o[path[i]] = true;

2 Comments

much worse, it does NOT throw if a key exist, but isn't an object (check {a1:{a2:999}}).
Yeah, for that you'd need to use if (Object(o) !== o) throw new TypeError("Did encounter non-object, not going to get/set properties on this");.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.