Search Recursively for Value in Object by Property Name

Search recursively for value in object by property name

You could 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'));

Recursively searching for a property in object

You could use a recursive approach by checking if the property exist, otherwise call iter again with the parent key.

function iter(object) {    return 'property' in object ? object.property : iter(object.parent);}
var object = { parent: { parent: { parent: { parent: { property: 42 } } } } };
console.log(iter(object));

How to find the key of a value in a nested object recursively

After the few questions made above, it looks like the function should:

  • Assume the input is always an object.
  • Assume it might encounter arrays in its way.
  • Assume it must stop after meeting one value (in case multiple value exists).

The provided input code given by the OP does not handle array cases.

Below code is sampled to work with these sample cases:

  • Plain nested object structure.
  • Object with nested arrays of objects or elements.

Below function accepts a second argument which is a callback to evaluate whether the element met is actually the one we're looking for. In this way, it's easier to handle more complex checks.

The recursive approach is kept and, once the key is met, the function simply return to avoid unnecessary searchs.

const foo = { data: { data2: { data3: 'worked' }, data21: 'rand' }, data01: 'rand01' };const fooWithArrays = {  data: {    data2: {      data3: 'not here'    },    data4: [      { data5: 'worked' },      { data6: 'not me' }    ]  }};const fooWithExpression = {  data: {   data2: {    data3: { id: 15, name: 'find me!' }   },   data21: {    data25: 'not me'   }  }};
const findKeyByValue = (obj, equalsExpression) => { // Loop key->value pairs of the input object. for (var [key, v] of Object.entries(obj)) { // if the value is an array.. if (Array.isArray(v)) { // Loop the array. for (let i = 0; i < v.length; i++) { // check whether the recursive call returns a result for the nested element. let res = findKeyByValue(v[i], equalsExpression); // if so, the key was returned. Simply return. if (res !== null && res !== undefined) return res; } } // otherwise.. else { // if the value is not null and not undefined. if (v !== null && v !== undefined) { // if the value is an object (typeof(null) would give object, hence the above if). if (typeof(v) === 'object') { // check whether the value searched is an object and the match is met. if (equalsExpression(v)) return key; // if not, recursively keep searching in the object. let res = findKeyByValue(v, equalsExpression); // if the key is found, return it. if (res !== null && res !== undefined) return res; } else { // finally, value must be a primitive or something similar. Compare. let res = equalsExpression(v); // if the condition is met, return the key. if (res) return key; // else.. continue. } } else continue; } }}
console.log( findKeyByValue(foo, (found) => found === 'worked') );console.log( findKeyByValue(fooWithArrays, (found) => found === 'worked') );console.log( findKeyByValue(fooWithExpression, (found) => found && found.id && found.id === 15) );

Recursively looping through an object to build a property list

I made a FIDDLE for you. I am storing a stack string and then output it, if the property is of primitive type:

function iterate(obj, stack) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property], stack + '.' + property);
} else {
console.log(property + " " + obj[property]);
$('#output').append($("<div/>").text(stack + '.' + property))
}
}
}
}

iterate(object, '')


Update: 17/01/2019

There used to be a different implementation, but it didn't work. See
this answer
for a prettier solution

How to recursively get access to object values?

I think the reason you're having problems with the arrays is because you test typeof value === 'object', but that will be true if value in an Array.

Here, I write what I think is a simpler version of recursiveNumbersSearch, called deepProp. It takes a property name and returns a function that takes an object and does a depth-first traversal, retrieving the named property wherever it's found. We can then use that to layer on simple functions to sum the rating properties or
flatten and sort the justNumbers ones, like this:

const deepProp = (prop) => ({[prop]: p, ...rest}) =>
p == undefined
? Object .values (rest) .flatMap (deepProp (prop))
: [p, ... deepProp (prop) (rest)]

const a = [{person: [{person: [{person: [], rating: 512, justNumbers: [30, 15, 327]}, {person: [{person: [], rating: 538, justNumbers: [13, 55, 643]}, {person: [], rating: 964, justNumbers: [314, 523, 512]}], rating: 413, justNumbers: [876, 541, 623]}], rating: 176, justNumbers: [842, 812, 643]}], rating: 235, justNumbers: [33, 565, 73]}]

console .log (deepProp ('rating') (a))
console .log (deepProp ('justNumbers') (a))

const sum = (ns) => ns .reduce ((a, b) => a + b, 0)

const sumRatings = (o) => sum (deepProp ('rating') (o))
const organizeNumbers = (o) => deepProp ('justNumbers') (o) .flat () .sort ((a, b) => a - b)

console .log (sumRatings (a))
console .log (organizeNumbers (a))
.as-console-wrapper {max-height: 100% !important; top: 0}

Search recursively through a deeply nested object/array and collect values that include a specified string without knowing the key(s)

This recursive function finds those leaf nodes that match a predicate:

const getMatches = (pred) => (obj) =>
obj == null ?
[]
: Array .isArray (obj)
? obj .flatMap (getMatches (pred))
: typeof obj == 'object'
? Object .values (obj) .flatMap (getMatches (pred))
: pred (obj)
? obj
: []

const array = [{key1: 'dog', key2: 'cat'},{key1: 'dog', key2: 'cat', key3: [{keyA: 'mouse', keyB: 'https://myURL.com/path/to/file/1', keyC: [{keyA: 'tiger', keyB: 'https://myURL.com/path/to/file/2'}]}]}, {key4: 'dog',key5: 'https://myURL.com/path/to/file/3'}]

const check = val => typeof val == 'string' && val .includes ('https://myURL.com')

console .log (
getMatches (check) (array)
)

Recursively search for a value in global variables and its properties

Deep search but without the recursive function calls

Functional recursion has internal stack limits and wastes memory.

Additional features added

Recursive object protection in the form of a searched array; It doesn't use up too much memory of course as the objects are only stored as references.

Return true if the the object itself matches the value. Otherwise it would return '' which would match to false.

Arrays use angle-bracket notation.

The code

function globalSearch(startObject, value) {
var stack = [[startObject,'']];
var searched = [];
var found = false;

var isArray = function(test) {
return Object.prototype.toString.call( test ) === '[object Array]';
}

while(stack.length) {
var fromStack = stack.pop();
var obj = fromStack[0];
var address = fromStack[1];

if( typeof obj == typeof value && obj == value) {
var found = address;
break;
}else if(typeof obj == "object" && searched.indexOf(obj) == -1){
if ( isArray(obj) ) {
var prefix = '[';
var postfix = ']';
}else {
var prefix = '.';
var postfix = '';
}
for( i in obj ) {
stack.push( [ obj[i], address + prefix + i + postfix ] );
}
searched.push(obj);
}
}
return found == '' ? true : found;
}

Problems

Without passing the initial variable name into the function, we can't return the fully qualified variable name from the beginning. I can't think of a solution and I would be surprised if there was one.

Variable names with spaces are valid as the key to an object, as are other invalid variable names, it just means that the value must be addressed using angle-brackets. There are a couple of solutions I can think of. Regex check each variable name to make sure it's valid and use angle-brackets notation if it is not. The overriding problem with this is that the reg-ex is a page long. Alternatively, we could only use angle-brackets but this isn't really true to the OPs original question.

The indexOf call on the array 'searched' might be a bit heavy on very large objects but I can't yet think of an alternative.

Improvements

Apart from cleaning up the code a little, it would also be nice if the function returned an array of matches. This also raises another issue in that the returned array would not contain references to recursive objects. Maybe the function could accept a result format configuration parameter.

Angular/JS/Typescript- Recursively access properties in an object

You can do that with this simple recursive function which takes an array as the query:

const me = {
id: 1,
name: 'James',
age: 40,
family: {
mother: {
id: 101,
name: 'Bea',
age: 66
},
father: {
id: 102,
name: 'Martin',
age: 69
},
children: [
{
id: 11,
name: 'Tom',
age: 18,
},
{
id: 12,
name: 'Nancy',
age: 13,
},
],
},
}

function search(obj, [first, ...rest]) {
return rest.length ? search(obj[first], rest) : obj[first];
}

const result = search(me, ['family', 'father', 'age']);

console.log(result);

C# How to search property name recursively

I think there are some issues with searching properties that come from system modules. You have to decide which properties are worth recursively descending and which ones are not. Also, you'll have to maintain a list of objects that you have already visited to ensure that you do not follow cycles. I think a breadth-first search would be best, but for this example, I'll code a depth-first search. Also, I just return the first match, not all matches, you can adjust as needed. Furthermore, it returns a (mostly useless) string version of the path rather than a list of reflected properties that would be needed to actually access it (You'd have to do reflection again to locate the properties by name to retrieve the value from this "path" string.)

I'll start you off with a basic implementation. Likely someone else can improve upon it.

static string GetPropertyPath(object obj, string name, List<object> visited = null)
{
// does the object have the property?
Type t = obj.GetType();
var properties = t.GetProperties();
foreach (var property in properties) {
if (property.Name == name) {
// that's it!
return name;
}
}

// if we get here, it's because we didn't find the property.
if (visited == null) {
visited = new List<object>();
visited.Add(obj);
}

// Get all the properties of the first object and keep searching,
// keeping track of objects we've visited already.
foreach (var property in properties) {
// Limit which kinds of properties we search
if (object.ReferenceEquals(typeof(Program).Module, property.Module)) {
// get the value of the property
object obj2 = property.GetValue(obj);
// Do not search any previously visited objects
if (!visited.Any(o => object.ReferenceEquals(o, obj2))) {
visited.Add(obj2);
string path = GetPropertyPath(obj2, name, visited);
if (path != null) {
// found it!
return property.Name + "." + path;
}
}
}
}
return null;
}

Example

static void Main(string[] args)
{
var teste = new Teste();
teste.Descricao = "bar";
teste.Time = new Time("foo");
var b = GetPropertyPath(teste, "Nome"); // "Time.Nome"
}


Related Topics



Leave a reply



Submit