GibboK
GibboK

Reputation: 73928

How to get a nested property for an object dynamically?

I need to create a function that search a property in an object and returns its value. Object could have an arbitrary structure with property nested in other objects.

How could I change my script?

var item = {
    id: 10,
    properties: {
        parent_id: 20,
        x: {
            y: 100
        }
    }
}

function getValue(what) {
    console.log(item[what]);

}

getValue('id');
// ok return 10

getValue('properties.parent_id')
// undefined - ISSUE here I would like to have returned 20    

Upvotes: 2

Views: 251

Answers (4)

AL-zami
AL-zami

Reputation: 9066

for a deeply nested object you can use a recursive function to retrieve all the object which are nested inside parent Object.It can be applied to an object literal having three to more number of nested object

  var parentObj = {
     parentProp: 10,
       childObj: {
           childProp: 20,
                    grandChildObj: {
                       y: {
                          z:'lol',
                          places:['newyork','canada','dhaka']
                       }
                    }
                }
      }


var arr=[];
var container=[];
container.push(parentObj);
var count=0;

function getObjNum(obj){  //<= recursive function to retrieve all the nested objects inside parent object

      var prop=Object.getOwnPropertyNames(obj);
      for(i=0;i<prop.length;i++){
          if(typeof(obj[prop[i]])=='object'){
          if(!Array.isArray(obj[prop[i]])){
            container.push(obj[prop[i]]);
            count++;
            getObjNum(obj[prop[i]]); // recursive call to getObjNum
          }
          }

      }
}
getObjNum(parentObj); // sent the parent object to getObjNum

function getVal(str){
    var split=str.split('.');

    container.forEach(function(obj){

        if(obj.hasOwnProperty(split[split.length-1])){

        console.log(obj[split[split.length-1]]);
        }


    });
}
getVal('parentObj.parentProp');
getVal('y.z');

Upvotes: 0

6502
6502

Reputation: 114529

You need to create a "path", i.e. a sequence, of keys too access in order. One way is to choose an uncommong separator that is never going to be used for object keys, e.g. |:

element = obj;
path.split("|").forEach(function(key){
    element = element[key];
});

if you cannot exclude any char from the keys then supporting escaping is mandatory; for example you could use , to separate keys but allowing @, to mean a comma is part of the key and @@ meaning an at-sign is part of the key.

element = obj;
(path+",").match(/([^@,]|@.)*,/g).forEach(function(x){
    element = element[x.slice(0,-1).replace(/@(.)/g, "$1")];
});

for example the path "1,2,x,y@,z,,w@@" can be used to access

 obj[1][2].x["y,z"][""]["w@"]

Upvotes: 2

suish
suish

Reputation: 3343

The code below makes flat obj so to access like that.

    var flatten = function(obj,into,prefix){
        into = into ||{};
        prefix = prefix || '';

        _.each(obj,function(val,key){
            if(obj.hasOwnProperty(key)){
                if(val && typeof val === 'object'){
                    flatten(val,into,prefix + key + '.');
                }else{
                    into[prefix + key] = val;
                }
            }
        });
        return into;
    };

The working JSFiddle is here http://jsfiddle.net/fper2d73/

The complete code is

    var item = {
        id: 10,
        properties: {
            parent_id: 20,
            x: {
                y: 100
            }
        }
    }

    var flatten = function(obj,into,prefix){
        into = into ||{};
        prefix = prefix || '';

        _.each(obj,function(val,key){
            if(obj.hasOwnProperty(key)){
                if(val && typeof val === 'object'){
                    flatten(val,into,prefix + key + '.');
                }else{
                    into[prefix + key] = val;
                }
            }
        });
        return into;
    };


    var _item = flatten(item);

    function getValue(what) {
        console.log(_item[what]);

    }

    getValue('id');
    // returns 10

    getValue('properties.parent_id')
    // returns 20

    getValue('properties.x.y')
    //returns 100

Upvotes: 0

Oday Fraiwan
Oday Fraiwan

Reputation: 1157

You can provide a syntax to access these properties in the getValue function parameter. For example, to access properties.parent_id you can use 'properties.parent_id'.

Then the getValue function should be written as the following:

function getValue(prop) {
    if (typeof(prop) !== 'string')
        throw 'invalid input string';

    props = prop.split('.');
    var value = item[props[0]];
    for(var i = 1, l = props.length; i < l; i++) {
        value = value[props[i]];
    }
    return value;
}

Example:

getValue('properties.parent_id'); //returns 20

Upvotes: 3

Related Questions