4

Is it possible in one line to add a property to an added property of an added property of ...

var some_object = {};
some_object.prop_a = "hi";
some_object.prop_b.prop_c.prop_d = "ho";

while line 2 obviously works, line 3 doesnt as it returns the cannot read property of undefined error. I know, that prop_b and prop_c dont exist when prop_d should be assigned. But is there a simple one-liner to do what i want to do (i.e. add a nested property even if some levels in the nested object dont exist yet)?

weidler
  • 676
  • 1
  • 6
  • 22
  • No, there isn't. You must define the objects you're writing to. – BenM May 26 '16 at 21:13
  • 2
    Out of the box, no, but you could certainly write a function that would take a chain of properties and an object and add empty objects as needed. – Matt Burland May 26 '16 at 21:14
  • Check http://stackoverflow.com/questions/37437805/convert-map-to-json-object-in-javascript/37438729?noredirect=1#comment62398771_37438729. – IMTheNachoMan May 26 '16 at 21:20
  • hopefully coming soon to more browsers! This could be achieved using a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) – Trey May 26 '16 at 21:25

4 Answers4

4

You could certainly write a function to do this. Something like this perhaps:

function addPropertyChain(chain, val, obj) {
    var propChain = chain.split(".");
    if (propChain.length === 1) {
        obj[propChain[0]] = val;
        return;
    }
    var first = propChain.shift();
    if (!obj[first]) {
        obj[first] = {};
    }    
    addPropertyChain(propChain.join("."), val, obj[first] );
}

var some_object = {};
addPropertyChain("prop_b.prop_c.prop_d","ho",some_object);

console.log(some_object);
Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • I think we had the same thoughts about this problem. good job. – Ryan May 26 '16 at 21:32
  • Thanks a lot! Thought about writing a function myself if there is no other way. Yours is pretty handy and will be used! – weidler May 26 '16 at 21:33
3

I know, that prop_b and prop_c dont exist when prop_d should be assigned.

If this is the case, you can do this:

some_object.prop_b = {
  prop_c : {
    prop_d : "ho"
  }
};

(Or, in one line:)

some_object.prop_b = { prop_c : { prop_d : "ho" } };

You're just using JavaScript's object initializer syntax to create nested objects.

However, beware as this would overwrite any existing prop_b or prop_b.prop_c values, if those values already existed.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
0

You could call a method to ensure the slot is an object before assigning. A disadvantage is that you have supply the path as a string. You'd have to walk the chain.

function ensurePropertyOnObject(o, m)
{
    var props = m.split('.');
    var item;
    var current = o;

    while(item = props.shift()) {
        if(typeof o[item] != 'object') {
            current[item] = {};
        }
        current = o[item];
    }    
}

ensurePropertyOnObject(some_object, "prop_b.prop_c.prop_d");
some_object.prop_b.prop_c.prop_d = "ho";
console.log(some_object);

See an example: https://jsfiddle.net/ho4k02fr/

Ryan
  • 14,392
  • 8
  • 62
  • 102
0

To dynamically access and set object properties i had invented two Object methods called Object.prototype.setNestedValue() and Object.prototype.getNestedValue() In which you provide the nested properties as arguments. For the Object.prototype.setNestedValue() the last argument is the value to be set at the end. Goes like this;

Object.prototype.getNestedValue = function(...a) {
  return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};

Object.prototype.setNestedValue = function(...a) {
  return a.length > 2 ? typeof this[a[0]] === "object" && this[a[0]] !== null ? this[a[0]].setNestedValue(...a.slice(1))
                                                                              : (this[a[0]] = typeof a[1] === "string" ? {} : new Array(a[1]),
                                                                                 this[a[0]].setNestedValue(...a.slice(1)))
                      : this[a[0]] = a[1];
};

var some_object = {};
some_object.prop_a = "hi";
some_object.setNestedValue("prop_b","prop_c","prop_d","ho");
console.log(JSON.stringify(some_object,null,2));

You can also add array objects. For that you have to enter the index in number type though. Lets have an example...

Object.prototype.getNestedValue = function(...a) {
  return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};

Object.prototype.setNestedValue = function(...a) {
  return a.length > 2 ? typeof this[a[0]] === "object" && this[a[0]] !== null ? this[a[0]].setNestedValue(...a.slice(1))
                                                                              : (this[a[0]] = typeof a[1] === "string" ? {} : new Array(a[1]),
                                                                                 this[a[0]].setNestedValue(...a.slice(1)))
                      : this[a[0]] = a[1];
};

var some_object = {};
some_object.prop_a = "hi";
some_object.setNestedValue("prop_b",3,"prop_d","yay");
some_object.setNestedValue("prop_b",0,"prop_d","zero this is");
some_object.setNestedValue("prop_b",1,"prop_d","i am the one");
some_object.setNestedValue("prop_b","prop_c","prop_d","ho");
console.log(JSON.stringify(some_object,null,2));

You will notice that prop_c s not enlisted but that still exists as a property of the array assigned to prob_b and it's fully accessible.

Redu
  • 25,060
  • 6
  • 56
  • 76