0

I am trying to create a JavaScript object and define its childs on the fly. Therefore, the sub items are dynamic and they may change in run-time.

What I have? var _obj = {};

In the next step, I would like to set some variables in runtime using a function. I can not find an efficient work around to implement the following function:

setObjValue("spaceA.spaceB.id", 1); that save 1 in the following path:

_obj.spaceA.spaceB.id = 1;

I have tried to split the key based on ., and then create the path step by step using a loop. However, since we do not have reference in JavaScript, it is not possible to achieve this.

In other words, how to make a function to add an object in the following pattern?

_obj["spaceA"]["spaceB"]....["id"] = 1;

  • The number of root steps is not defined before run. So I can not manually use codes. I need to achieve this dynamically like the example function above.
  • 1
    this does opposite of what you want but may give you some inspiration on how to accomplish it http://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-with-string-key – Clay Dec 09 '15 at 02:48
  • @ClaytonSmith Unfortunately reading in a loop is possible, however writing is not possible as we can not benefit from Reference. So, in the save procedure (reverse of your mentioned topic) any change would be applied to a local variable, and there is no reference to the main object. –  Dec 09 '15 at 02:54

3 Answers3

2

Try this:

function setObjValue(object, path, value) {
    var pathArray = path.split('.');
    for(var i = 0; i < pathArray.length; i++) {
        var name = pathArray[i];
        if(i == pathArray.length-1) {
            object[name] = value;
        } else {
            if(!(name in object)) object[name] = {};
            object = object[name];
        }
    }
    return;
}

var obj = {};
setObjValue(obj, "test1.test2.sub", "123");
console.log(obj);
David Fang
  • 1,777
  • 1
  • 14
  • 19
  • Might want to move the `var`s to the top of the function so that scope is not incorrectly guessed. – jcaron Dec 09 '15 at 02:53
2

Recursion!

var startingObj = {};
var path = "one.two.three.four".split('.');
var val = 7;

function recursiveFun(o, p, v){
  var key = p.shift(); // get the current key

  if (p.length == 0){
    // set key to val and exit
    o[key] = val;
    return;
  }

  o[key] = {}; // should check if this exists before overwriting
  recursiveFun(o[key], p, v);
}

recursiveFun(startingObj, path, val);

http://plnkr.co/edit/TgoRS0xkXGG6J3DzFxzi?p=info

mgiesa
  • 1,023
  • 7
  • 9
  • 2
    Good solution, and with tail call optimizations in ES6, will be nearly as performant as the loop-based solutions. I recommend `p.shift()` instead of `p.splice(0, 1)`. Also, you should check if `o[key]` is defined before assigning `{}` to it (so you don't overwrite existing keys). – Igor Raush Dec 09 '15 at 03:17
  • Good call on using .shift(), I'll edit to add that. Checking for o[key] before overwriting something is definitely good practice but it can be handled different ways so I left that up to the OP. I'll add a comment pointing that out, though. – mgiesa Dec 09 '15 at 03:22
-1

This one should do it in the most effective way

function setObjValue(path, value) {
    path = path.split('.')
    var obj = path[0]
    //  make obj the root
    path.shift()
    //  remove root object from path
    while(path.length){
        //  move further in tree
        obj = obj[path[0]]
        obj.shift()
    }
    //  assign value
    obj = value
}
Akxe
  • 9,694
  • 3
  • 36
  • 71
  • I am not sure if this function works, it throws an error (I have not accurately reviewed the code) `setObjValue("spaceA.spaceB.id",1)`. –  Dec 09 '15 at 03:06
  • 1
    This will not work. For one, you are creating a local variable `obj`, assigning to it, and then never returning it, so you can't expect this function to have any effect on its enclosing scope. Also, you cannot modify an object by reference by assigning to its reference like `obj = value`. – Igor Raush Dec 09 '15 at 03:08