How do I search an array of nested objects for field names?

59 Views Asked by At

I'm looking for a couple of different ways to access the data below, using ES6 and/or lodash.

What I'm trying to do is to return the parent object, where device_id matches.

i.e, for each item in entities, if in any of the nested objects, the device_id field = abc, return the whole array item

const entities = [
    {
        "<unknown key>": {
            "entity_id": "something",
            "device_id": "abc"
        },
        "<unknown key>": {
            "entity_id": "else",
            "device_id": "abc"
        },
    },
    {
        "<unknown key>": {
            "entity_id": "hello",
            "device_id": "xyz"
        },
        "<unknown key>": {
            "entity_id": "world",
            "device_id": "xyz"
        }
    }
]
3

There are 3 best solutions below

0
Alexander Nenashev On BEST ANSWER

Use Array::find() to find the item with matching a child with the given device_id:

const res  = entities.find(o => Object.values(o).some(x => x.device_id === 'abc'));
console.log(res);
<script>
    const entities=[{"<unknown key>":{entity_id:"something",device_id:"abc"},"<unknown key2>":{entity_id:"else",device_id:"abc"}},{"<unknown key>":{entity_id:"hello",device_id:"xyz"},"<unknown key2>":{entity_id:"world",device_id:"xyz"}}];
</script>

If you are interested in speed, use for..in instead of Object.values().some(). That way you avoid an intermediate array:

` Chrome/120
-------------------------------------------------------------------
for..in               1.00x  |  x100000000  649  671  671  678  683
Object.values()       1.79x  |   x10000000  116  116  117  122  133
-------------------------------------------------------------------
https://github.com/silentmantra/benchmark `

const entities=[{"<unknown key>":{entity_id:"something",device_id:"abc"},"<unknown key2>":{entity_id:"else",device_id:"abc"}},{"<unknown key>":{entity_id:"hello",device_id:"xyz"},"<unknown key2>":{entity_id:"world",device_id:"xyz"}}];
    
// @benchmark Object.values()    

entities.find(o => Object.values(o).some(x => x.device_id === 'abc'));

// @benchmark for..in
entities.find(o => {
  for(const key in o){
    if(o[key].device_id === 'abc') return true;
  }
});

/*@end*/eval(atob('e2xldCBlPWRvY3VtZW50LmJvZHkucXVlcnlTZWxlY3Rvcigic2NyaXB0Iik7aWYoIWUubWF0Y2hlcygiW2JlbmNobWFya10iKSl7bGV0IHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic2NyaXB0Iik7dC5zcmM9Imh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9naC9zaWxlbnRtYW50cmEvYmVuY2htYXJrL2xvYWRlci5qcyIsdC5kZWZlcj0hMCxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHQpfX0='));

0
Unmitigated On

Use Array#filter in conjunction with Array#some to get all matches. If only one match is desired, replace filter with find.

let search = 'abc';
const entities=[{"<unknown key>":{entity_id:"something",device_id:"abc"},"<unknown key2>":{entity_id:"else",device_id:"abc"}},{"<unknown key>":{entity_id:"hello",device_id:"xyz"},"<unknown key2>":{entity_id:"world",device_id:"xyz"}}];
const res  = entities.filter(o => Object.values(o)
                .some(x => x.device_id === search));
console.log(res);

0
gog On

Here's some generic code:

function findPath(obj, fn, path = []) {
    if (typeof obj !== 'object')
        return

    if (fn(obj))
        return path
    
    for (let [k, v] of Object.entries(obj)) {
        let p = findPath(v, fn, path.concat([v]))
        if (p)
            return p
    }
}

Applied to your object like this:

findPath(entities, x => x.device_id === 'xyz')

this will return an array of the matching object and all intermediate objects.

Replace concat([v]) with concat([k]) if you want a list of keys instead.