Redsandro
Redsandro

Reputation: 11356

What is the best way to get/set an objects deep property in Javascript?

It is notoriously annoying to get a deeper property from an object. If the parent does not exist, you get an error. So you need to stop getting to the property when a parent doesn't exist.

The clueless way to do this is:

if (parent.value && parent.value.subvalue && parent.value.subvalue.targetvalue)
    var myVal = parent.value.subvalue.targetvalue;
else
    var myVal = null;

When you need to get a property often, this is unworkable and you need a shortcut function. First I did something like this:

function getProp(path) {
    try {
        return eval(path);
    }
    catch (e) {
        return null;
    }
};
// getProp('parent.value.subvalue.targetvalue');

But this is lame for at least two reasons: The object must be in the scope, and no one likes to use eval().

So maybe it's better to apply the object to the function:

function getProp(path, parent) {
    path = path.split('.');

    var val = parent;

    for (var k = 0; k < path.length; k++) {
        var key = path[k];

        try {
            val = val[key];
        }
        catch (e) {
            return null;
        }
    }

    return val;
};
// getProp('value.subvalue.targetvalue', parent);

But somehow it still doesn't feel right.
How do you do this? What is best practice?

Setting an objects deep property of which the parents may or may not exist is even more annoying.

parent = parent || {};
parent.value = parent.value || {};
parent.value.subvalue = parent.value.subvalue || {};
parent.value.subvalue.target = "YAY SOME VALUE!"

How would you tackle this nicely?

Are there javascript native functions for this yet, since this needs to be done often?

Upvotes: 1

Views: 1678

Answers (3)

Shishir Arora
Shishir Arora

Reputation: 5923

with es6:

const getProp = (property, obj) =>property.split('.').reduce((t, p)=>t?.[p], obj);

console.log(getProp('a.b.d', {a: {b: {d: 'ans'}}}));
console.log(getProp('a.e.e', {a: {b: {d: 'does not exist'}}}));

Upvotes: 1

RobG
RobG

Reputation: 147373

Instead of try..catch, just test for the property:

function getProp(path, parent) {
    path = path.split('.');

    for (var k=0, kLen=path.length; k<kLen; k++) {

        if (parent.hasOwnProperty(path[k])) {
            parent = parent[path[k]];

        } else {
          return; // undefined? null? error?
        }
    }
    return parent;
}

var obj = {a: {b: {c: 'C', d:'D'}}};

console.log(
  getProp('a.b.d', obj)  // D
);      

Upvotes: 2

gustavohenke
gustavohenke

Reputation: 41440

Builtin ways, no. If you're in Node.js platform you can try some package like dotty.

Anyway, the way I think you could do it is somewhat like this (I haven't tested it! But I think it could work):

key.split( "." ).reduce(function( memo, part ) {
   if ( typeof memo !== "object" || memo[ part ] === undefined ) {
      return;
   }

   return memo[ part ];
}, obj );

Upvotes: 4

Related Questions