4

Say I want to assign a value like this:

x.label1.label2.label3 = someValue;
// or equivalently:
x['label1']['label2']['label3'] = someValue;

This works as long as x.label1.label2 is defined but runs into reference errors otherwise. Which makes sense of course. But is there an easy way to assign this anyway where it simply creates the necessary nested objects?

So for example, if x equals { label1: {}, otherLabel: 'otherValue' } I want to update x to become { label1: { label2: { label3: someValue } }, otherLabel: otherValue }

I think I might be able to write a function myself, but is there a language feature or standard library function that does this?

Paul Visschers
  • 584
  • 2
  • 10

5 Answers5

1

is there a language feature or standard library function that does this

No. You have to write your own function or use a library that provides such functionality.

Related: How to set object property (of object property of..) given its string name in JavaScript?

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
1

This is partially possible using the Proxy class. You can wrap your object in a Proxy and override the get trap to create another copy of the same proxy when you access a nonexistent property. This lets you recursively create "deep" properties. An example:

let traps = {
    get: function (target, name) {
        if (!(name in target))
            target[name] = new Proxy({}, traps);

        return target[name];
    }
};

let x = new Proxy({}, traps);

Then you would use x like any object, except it has this special behavior:

x.label1.label2.label3 = 'foo';

which creates a nested hierarchy of objects. However, note that this will create an object even if you access a nonexistent property. Thus, you will have to use the in keyword to check if it really contains a given property.

rvighne
  • 20,755
  • 11
  • 51
  • 73
1

I think you should indeed use a custom function such as:

function assignByPath(obj, path, value) {
  var field = path.split('>'),
      last = field.pop();

  field.reduce(
    function(node, f) {
      return node[f] = node[f] instanceof Object ? node[f] : {};
    }, obj
  )[last] = value;
}

var myObj = {};
assignByPath(myObj, 'label1>label2>label3', 'someValue');

console.log(myObj);

Theoretically, you could also override Object.prototype, which would allow you to do:

myObj.assignByPath('label1>label2>label3', 'someValue');

But I would not recommend that.

Arnauld
  • 5,847
  • 2
  • 15
  • 32
1

You can use Array.prototype.shift(), Object.assign(), recursion

var x = {
  label1: {},
  otherLabel: "otherValue"
};

var nestprops = (props, value, obj, o, curr = props.shift()) => props.length 
  ? nestprops(props, value, (Object.assign(obj, {[curr]: {}}) && obj[curr]), o) 
  : ((!value || value) && (obj[curr] = value) && o);

console.log(nestprops(["label1", "label2", "label3"], "someValue", x, x));
guest271314
  • 1
  • 15
  • 104
  • 177
0

Check length of keys inside label1 object if its equal to 0 then modify it to your desired object.

Here is a snippet, hope it helps.

var obj = { label1: {}, otherLabel: 'otherValue' };

if(Object.keys(obj.label1).length == 0 ) {
    obj.label1 = { label2: { label3: "value3" } };
}
console.log(obj);
M.Tanzil
  • 1,987
  • 1
  • 12
  • 28