Search recursively for value in object by property name

I am creating a utility function that should look for the name of a property and return its value after it is detected. He should do it recursively:

// Function util.findVal = (object, propName) => { for (let key in object) { if (key === propName) { console.log(propName) console.log(object[key]) return object[key] } else { util.findVal(object[key], propName) } } } // Input object: { photo: { progress: 20 } } // Usage util.findVal(object, 'progress') 

However, the console log works forever and the browser crashes. What am I doing wrong?

EDIT:

This is how I call the function:

 // Input item: { photo: { file: {}, progress: 20 } } this.findProgress(item) methods: { findProgress (item) { return util.findVal(item, this.propName) } } 
+12
source share
12 answers

You can use Object.keys and iterate with Array#some .

 function findVal(object, key) { var value; Object.keys(object).some(function(k) { if (k === key) { value = object[k]; return true; } if (object[k] && typeof object[k] === 'object') { value = findVal(object[k], key); return value !== undefined; } }); return value; } var object = { photo: { progress: 20 }}; console.log(findVal(object, 'progress')); 
+19
source

There are several errors in your code:

  • You util.findVal recursively, but do not return the result of the call. The code should be return util.findVal(...)
  • You are not passing the attribute name key recursive call
  • You are not processing the link loop feature
  • If the object contains a key, as well as a subobject containing a key, the return value is random (depends on the sequence in which the keys are analyzed)

The third problem is what can cause infinite recursion, for example:

 var obj1 = {}, obj2 = {}; obj1.x = obj2; obj2.y = obj1; 

if you just keep looking for a recursive search in obj1 or obj2 , this can lead to infinite recursion.

Unfortunately, for reasons unclear to me in Javascript, it is not possible to recognize an “identity” object ... (which is what Python id(x) does), you can only compare the object with another. This means that in order to find out if an object was already visible in the past, you need to perform a linear scan with known objects.

ES6 adds the ability to verify the identity of an object using Set and Map , where objects can be used as keys. This allows faster (sublinear) search time.

For example, a search solution that runs in order of depth:

 function findVal(obj, key) { var seen = new Set, active = [obj]; while (active.length) { var new_active = [], found = []; for (var i=0; i<active.length; i++) { Object.keys(active[i]).forEach(function(k){ var x = active[i][k]; if (k === key) { found.push(x); } else if (x && typeof x === "object" && !seen.has(x)) { seen.add(x); new_active.push(x); } }); } if (found.length) return found; active = new_active; } return null; } 

taking into account the object and the attribute name, it returns all the values ​​found with this name at the first depth of their detection (there can be several values: for example, when searching for {x:{z:1}, y:{z:2}} key "z" two values ​​are at the same depth).

The function also correctly processes structures that refer to themselves, avoiding an endless search.

+5
source

try replacing the else command as follows

return util.findVal(object[key],propName)

+2
source

Think about it if the key is not found.

I think you could do something like this instead of searching

 return object[propName] || null 

There was no breakpoint in your code, I assume that you are trying to search inside the entire object, not just directly related attributes, so here you can edit the code

EDIT:

 util.findVal = (object, propName) =>{ if(!!object[propName]){ return object[propName] }else{ for (let key in object) { if(typeof object[key]=="object"){ return util.findVal(object[key], propName) }else{ return null } } } } 
+1
source

I think you say you want to find the name of the property anywhere recursively in the tree of property objects and sub-properties. If so, here is how I approach this:

 var object1 = _getInstance(); // somehow we get an object var pname = 'PropNameA'; var findPropertyAnywhere = function (obj, name) { var value = obj[name]; if (typeof value != 'undefined') { return value; } foreach(var key in obj) { var v2 = findPropertyAnywhere(obj[key], name); if (typeof v2 != 'undefined') { return v2; } } return null; } findPropertyAnywhere(object1, pname); 
+1
source

I found this question in the field of the need for a general solution to check whether an object contains a specific value somewhere in its hierarchy (regardless of the key), which, of course, can include arrays. Thus, the following does not answer OPs questions directly or does not improve other solutions, but may help others find the same thing I did and find this post:

 function hasValue(object, value) { return Object.values(object).some(function(val) { if (val === value) { return true; } if (val && typeof val === 'object') { return hasValue(val, value); } if (val && typeof val === 'array') { return val.some((obj) => { return hasValue(obj, value); }) } }); } 

this is of course inspired by @Nina Scholz's solution!

+1
source

The answer depends on how difficult you want to get. For example, the analyzed JSON array does not contain functions - and I am sure that it will not contain the property value set by the parent node in the object tree.

This version returns the property value of the first property name found during a search in the object tree. undefined returned if either the named property was not found or is undefined . Changes to the difference will require some changes. It does not re-search for parent nodes that are already searching, and does not try to scan for null objects!

 let util = {}; util.findVal = (object, propName, searched=[]) => { searched.push( object) for (let key in object) { if (key === propName) { return object[key] } else { let obj = object[ key] if( obj && (typeof obj == "object" || typeof obj == "function")) { if( searched.indexOf(obj) >=0) { continue } let found = util.findVal(obj, propName, searched) if( found != searched) { return found } } } } searched.pop(); // not in object: return searched.length ? searched : undefined } 
0
source

I know this is an old post, but I found it helpful to answer the problem I ran into with recursively finding the value for this key. Once again I developed the answer given by Nina Scholz and came up with the following. It should be faster, because it does not create an array of all keys every time it is recursively called. In addition, this will explicitly return false if the key is not found.

 function findVal(obj, keyToFind) { if (obj[keyToFind]) return obj[keyToFind]; for (let key in obj) { if (typeof obj[key] === 'object') { const value = findVal(obj[key], keyToFind); if (value) return value; } } return false; } var object = { photo: { progress: 20 }}; console.log(findVal(object, 'progress')); 
0
source

I finished writing this function. This is a refactoring of the function found here: a recursive loop on an object to build a list of properties

added depth parameter to avoid in Chrome Devtools.

 function iterate(obj, context, search, depth) { for (var property in obj) { if (Object.prototype.hasOwnProperty.call(obj, property)) { if(typeof obj[property] == 'function') continue; if( property == search ){ console.log(context+property); return; } if (typeof obj[property] == "object" && depth < 7) { //console.log('--- going in: ' + context+property); iterate(obj[property], context+property+'.', search, depth+1); } /*else { console.log(context+property); }*/ } } } 
0
source

Returns the value of the field with the specified name.

data is the root node / object. keyName is the string name of the field / member.

If keyName indicates a field that is itself an object, this object is returned.

 function find (data, keyName) { for (const key in data) { const entry = data[key] if (key === keyName) return entry if (typeof entry === 'object') { const found = find(entry, keyName) if (found) return found } } } 

A for loop goes through each field, and if that field is an object, it will return to that object.

0
source

Do not write your own utility if you can avoid it.

Use something like jsonpath

Some examples of supported syntax:

 JSONPath Description $.store.book[*].author The authors of all books in the store $..author All authors $.store.* All things in store, which are some books and a red bicycle $.store..price The price of everything in the store $..book[2] The third book $..book[(@.length-1)] The last book via script subscript $..book[-1:] The last book via slice $..book[0,1] The first two books via subscript union $..book[:2] The first two books via subscript array slice $..book[?(@.isbn)] Filter all books with isbn number 
0
source

An old question, but to check if a property exists anywhere in the hierarchy of an object, try this simple option

 var obj = { firstOperand: { firstOperand: { firstOperand: { sweptArea: 5 } } } }; function doesPropertyExists ( inputObj, prop ) { return JSON.stringify(obj).indexOf( "\""+ prop +"\":" ) != -1; }; console.log( doesPropertyExists( obj, "sweptArea" ) ); console.log( doesPropertyExists( obj, "firstOperand" ) ); console.log( doesPropertyExists( obj, "firstOperand22" ) ); 
-1
source

Source: https://habr.com/ru/post/1259753/


All Articles