4

How do i get object property using array of string (name of properties)? (the last element in array is the inner property of object)

See the code below:

Handy way:

let myObject = {
    "property": {
        "subproperty": {
            "targetproperty": "Hi, We done it!"
        }
    }
};
let myString = "property:subproperty:targetproperty";
let parts = myString.split( ":" );
console.log( myObject[ parts[ 0 ] ][ parts[ 1 ] ][ parts[ 2 ] ] ); // Output: "Hi, We done it!"

Eval way:

let myObject = {
    "property": {
        "subproperty": {
            "targetproperty": "Hi, We done it!"
        }
    }
};
let myString = "property:subproperty:targetproperty";
let parts = myString.split( ":" );
let code = "myObject";
for ( let i = 0; i < parts.length; i++ ) {
    code += "['" + parts[ i ] + "']";
}
code += ";";
console.log( code );
console.log( eval( code ) ); // Output: "Hi, We done it!"

Eval is evil. so i need a cleaner way to do it.

How do i do it without eval and handy job?

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
Hasan Bayat
  • 926
  • 1
  • 13
  • 24

7 Answers7

9

You can use .reduce():

let myObject = {
  "property": {
    "subproperty": {
      "targetproperty": "Hi, We done it!"
    }
  }
};
let myString = "property:subproperty:targetproperty";
let value = myString.split(":").reduce(function(obj, prop) {
  return obj && obj[prop];
}, myObject);

console.log(value);
JLRishe
  • 99,490
  • 19
  • 131
  • 169
3

For loop:

function getByValue(arr, value) {

  for (var i=0, iLen=arr.length; i<iLen; i++) {

    if (arr[i].b == value) return arr[i];
  }
}

.filter

function getByValue2(arr, value) {

  var result  = arr.filter(function(o){return o.b == value;} );

  return result? result[0] : null; // or undefined

}

.forEach

function getByValue3(arr, value) {

  var result = [];

  arr.forEach(function(o){if (o.b == value) result.push(o);} );

  return result? result[0] : null; // or undefined

}

If, on the other hand you really did mean for..in and want to find an object with any property with a value of 6, then you must use for..in unless you pass the names to check. e.g.

function getByValue4(arr, value) {
  var o;

  for (var i=0, iLen=arr.length; i<iLen; i++) {
    o = arr[i];

    for (var p in o) {
      if (o.hasOwnProperty(p) && o[p] == value) {
        return o;
      }
    }
  }
}
Mrchipz
  • 31
  • 3
1

You can use a reduce solution:

var obj = {prop1: {prop2: {prop3: 'xpto'}}};
var props = ['prop1','prop2','prop3'];

var result = props.reduce((acc,val)=>acc[val],obj);
console.log(result);
Adilson de Almeida Jr
  • 2,761
  • 21
  • 37
  • `=>acc[val]` should be `=>acc && acc[val]` to check that `acc` is not `undefined`! – ibrahim mahrir Mar 17 '17 at 17:36
  • 1
    The accumulator of a `reduce` is never `undefined`. `accumulator: The value resulting from the previous call to callbackFn. On first call, initialValue if specified, otherwise the value of array[0].` See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce – Fabian S. Dec 13 '22 at 10:06
0

The recursive way ;)

Create a function that takes the current property, allparts and index.

Start with zero, return call to next index, try read and return with next call and increment index untill there are no more props to read/extract then return the value you got as the current property.

Let me know if you need working code

Tobias
  • 873
  • 9
  • 17
0

You can loop through your parts array, accessing the value of each key in each iteration.

function valueFromPath(obj, path) {
  for (var i = 0; i < path.length; ++i) {
    obj = obj[path[i]];
  }

  return obj;
};

valueFromPath(myObject, parts);

You probably want to clone the object first, in case you are going to use it for something else.


Alternatively, you can use traverse. Specifically traverse#getpath.

traverse(myObject).get(parts);
Justin Hellreich
  • 575
  • 5
  • 15
0

Here is a recursive approach, it will return undefined if the property isn't found:

const getPath = (o, keyPath, delimiter = '.') => {
    if (Array.isArray(keyPath)) {
        keyPath = keyPath.join(delimiter)
    }
  // o might not be an object when called recursively
  if(Object(o) === o) {
    let keys = keyPath.split(delimiter);
    let key  = keys.shift();

    if(o.hasOwnProperty(key)) {
      if(keys.length) {
        // there are more keys to check, call with attribute and remaining keys
        return getPath(o[key], keys.join(delimiter), delimiter);
      } else {
        // no more keys to check and object does have property
        return o[key];
      }
    }
    // didn't early return from having the key above, object does not have property
    return undefined;
  } else if(keyPath.length === 0) { 
    // o is not an object, but there is no remaining keyPath, so we will assume we've unwound the stack
    return o;
  }
  // not an object and keyLength is non-zero, object does not contain property
  return undefined;
};

let myObject = {
    "property": {
        "subproperty": {
            "targetproperty": "Hi, We done it!"
        }
    }
};

console.log(getPath(myObject, "property:subproperty:targetproperty", ":"));
Rob M.
  • 35,491
  • 6
  • 51
  • 50
0

You may do as follows;

function getNestedValue(o,...a){
  var val = o;
  for (var prop of a) val = typeof val === "object" &&
                                   val !== null     &&
                                   val[prop] !== void 0 ? val[prop]
                                                        : undefined;
  return val;
}

let myObject = {
    "property": {
        "subproperty": {
            "targetproperty": "Hi, We done it!"
        }
    }
};
let myString = "property:subproperty:targetproperty";

console.log(getNestedValue(myObject, ...myString.split(":")));
Redu
  • 25,060
  • 6
  • 56
  • 76