简体   繁体   中英

How to loop javascript object of objects and find a property

I have a sample object:

var test = {obj: check, oi: "5"  }        
var lord = {sol: "5"};
var holand = {vol: "try", mice: lord}
var second = {bol: "true"};
var check = { fol: holand}

my question is how to retrieve if object "test" has a property of "sol" which is in another object several layers deep. Should it be a somewhat recursive function? Is it possible to retrieve some kind of path to the property if the function returns true (property exists). In this case the path should be test["obj"]["fol"]["mice"]

Since originally answering this question, I was inspired to work on a library to do just this: koalaesce .

It works something like:

 var test = { obj: check, oi: "5" } var lord = { sol: "5" }; var holand = { vol: "try", mice: lord } var second = { bol: "true" }; var check = { fol: holand } function getDescendent(base, steps) { var step = base; for (var i = 0; i < steps.length; ++i) { if (step.hasOwnProperty(steps[i])) { step = step[steps[i]]; } else { throw new Error("Missing link at " + steps[i]); } } return step; } document.write(getDescendent(check, ["fol", "mice", "sol"])); try { document.write(getDescendent(check, ["fol", "tofu", "sol"])); } catch (e) { document.write(e); } 

The algorithm here starts from a base object and works its way down, going through an array of strings representing the property name of each step. It checks to make sure the current object has its own property ( not an inherited one) with the appropriate name, then walks down to that level. If the object does not, it throws with the missing name.

There are a number of occasions where you may want to remove the hasOwnProperty check, as inherited/prototype properties and methods may be useful (and it won't hurt anything). In your case, with simple objects, the behavior won't change. In most cases, not checking will allow you to access more properties.

For a more clever, ES6 solution, you can also use reduce and:

let test = {
  obj: check,
  oi: "5"
}
let lord = {
  sol: "5"
};
let holand = {
  vol: "try",
  mice: lord
}
let second = {
  bol: "true"
};
let check = {
  fol: holand
}

function getDescendent(base, ...steps) {
  return steps.reduce((prev, cur) => {
    if (prev && prev.hasOwnProperty(cur)) {
      return prev[cur];
    } else {
      throw new Error("Missing link at " + cur);
    }
  }, base);
}

document.write(getDescendent(check, "fol", "mice", "sol"));
document.write(getDescendent(check, "fol", "tofu", "sol"));

you can do a recursive loop by checking against the object's properties. this thread describes how to check if that property exists.

How do I check if an object has a property in JavaScript?

and yes you will have to keep track as you recurse if you want to know the path.

You must have a proper idea what you want to check. Ie the path down the object hierarchy. Because if you just started iterating all properties (keys) recursively, it won't be efficient at first place and there is a good chance of getting into looped references.

Nevertheless, you can query object properties, and property's properties down the hierarchy:

/**
 * Get a property defined under given object's hierarchy, matching given path.
 * @param obj Object to look into.
 * @param path filesystem style path for properties. E.g '/prop1/prop2/prop3'
 * @returns {*} Property under object hierarchy, or undefined.
 */
function getProp(obj,path){
    var props = path.split('/').reverse();
    var p;
    var o = obj;
    while( p = props.pop()){
        o = o[p]; // you can use your preferred scope. i.e own property only
        if(o == undefined){
            return o;
        }
    }

    return o;
}

/**
 * Utility function to check if a property is defined under given object's hierarchy, matching given path.
 * @param obj Object to look into.
 * @param path filesystem style path for properties. E.g '/prop1/prop2/prop3'
 * @returns {boolean} true if property is defined, false if property is undefined.
 */
function hasProp(obj, path){
    return getProp(obj,path) != undefined;
}


// Test object
var testObj = {
                a:{
                    b:{
                        c:'hello',
                        d:{
                            e:'world'
                        }
                    }
                }
              };

// valid property paths
console.log(hasProp(testObj,'a/b/c/'));
console.log(hasProp(testObj,'a/b/d/e'));
console.log(getProp(testObj,'a/b/c/'));
console.log(getProp(testObj,'a/b/d/e'));

// invalid property paths
console.log(hasProp(testObj,'a/d/c/'));
console.log(hasProp(testObj,'a/b/d/e/f'));
console.log(getProp(testObj,'a/d/c/'));
console.log(getProp(testObj,'a/b/d/e/f'));

Here is a (mostly) pure functional approach to recurse with some sort of 'condition':

/**
    Recurse through object until condition is met.
    @param {Array} objs An array of objects
    @return {Boolean} Whether condition is met.
*/
const recurseObjs = (objs, condition) => {
    const _subObjs = []
    for (let obj of objs) {
        for (let val of Object.values(obj)) {
            if (val === condition /*replace with some condition or multiple*/) return true // Stop if your condition is found
            else if (typeof val === 'object' && val !== null && Object.prototype.toString.call(val) !== '[object Array]') _subObjs.push(val) // Else recurse through subobjects that are not array or null
        }
    }
    if (_subObjs.length > 0) return recurseObjs(_subObjs, condition); 
    return false // Default return value if condition not found
}

recurseObjs([{foo:'bar'},{hello:{cruel:'world'}}],'world') // => true
recurseObjs([{foo:'bar'},{hello:{cruel:'world'}}],'test') // => false

If having the full path of the object's "found" subproperty value is needed, you can instead create a new key value _parent to add to the subproperty object. This _parent will be a string which holds the full subproperty path. It is updated on each recursion before being passed to the callback function. Then, _path can be returned instead of the true condition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM