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.
2
  • 1
    this does opposite of what you want but may give you some inspiration on how to accomplish it stackoverflow.com/questions/6491463/… Commented Dec 9, 2015 at 2: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. Commented Dec 9, 2015 at 2:54

3 Answers 3

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);
Sign up to request clarification or add additional context in comments.

1 Comment

Might want to move the vars to the top of the function so that scope is not incorrectly guessed.
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

2 Comments

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).
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.
-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
}

2 Comments

I am not sure if this function works, it throws an error (I have not accurately reviewed the code) setObjValue("spaceA.spaceB.id",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.