Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract deeply nested child objects by property name with lodash [duplicate]

I have arrays of deeply nested objects. I would like to write a function to extract arbitrary child objects from these arrays. In some cases values of nested properties are values and objects, in other cases they are arrays.

Examples of arrays are below:

[{parent: {level1: {level2: 'data'}}}]

[{parent: {level1: [{level2: {...}}, {level2: {...}}, {level2: {...}}]}}]

[{parent: {level1: [{level2: {level3: 'data'}}, {level2: {..}}, {level2: {..}}]}}]

Calling extraction function on such an array should result in an array of objects that we're interested in.

Examples of calling the function and its results for the example arrays above:

extractChildren(source, 'level2') = [{level2: 'data'}]

extractChildren(source, 'level2') = [{level2: {...}, level2: {...}, level2: {...}]

extractChildren(source, 'level3') = [{level3: 'data'}]

Is there a concise way to achieve this with lodash or I should use regular JavaScript to iterate through properties?

P.S. Think of it as equivalent of XPath select all nodes with the name "nodename"

like image 245
krl Avatar asked Feb 01 '16 18:02

krl


2 Answers

I hope, it helps:

'use strict';

let _ = require("lodash");
let source = [{parent: {level1: [{level2: {level3: 'data'}}, {level2: {}}, {level2: {}}]}}];

function extractChildren(source, specKey) {
    let results = [];
    let search = function(source, specKey) {
        _.forEach(source, function(item) {
            if (!!item[specKey]) {
                let obj = {};
                obj[specKey] = item[specKey];
                results.push(obj);
                return;
            }

            search(item, specKey);
        });
    };

    search(source, specKey);
    return results;
};

console.log(extractChildren(source, 'level3'));
// [ { level3: 'data' } ]
like image 143
Gergo Avatar answered Nov 04 '22 23:11

Gergo


From this question:

Elegant:

function find_obj_by_name(obj, key) {
    if( !(obj instanceof Array) ) return [];

    if (key in obj)
        return [obj[key]];

    return _.flatten(_.map(obj, function(v) {
        return typeof v == "object" ? find_obj_by_name(v, key) : [];
    }), true);
}

Efficient:

function find_obj_by_name(obj, key) {
    if( !(obj instanceof Array) ) return [];

    if (key in obj)
        return [obj[key]];

    var res = [];
    _.forEach(obj, function(v) {
        if (typeof v == "object" && (v = find_obj_by_name(v, key)).length)
            res.push.apply(res, v);
    });
    return res;
}
like image 1
rphv Avatar answered Nov 04 '22 23:11

rphv