How do I recursively search an object tree and return the matching object based on a key/value using JavaScript/Prototype 1.7

16,781

Solution 1

I went a slightly different route and made the findKey method an Object protype:

Object.prototype.findKey = function(keyObj) {
    var p, key, val, tRet;
    for (p in keyObj) {
        if (keyObj.hasOwnProperty(p)) {
            key = p;
            val = keyObj[p];
        }
    }

    for (p in this) {
        if (p == key) {
            if (this[p] == val) {
                return this;
            }
        } else if (this[p] instanceof Object) {
            if (this.hasOwnProperty(p)) {
                tRet = this[p].findKey(keyObj);
                if (tRet) { return tRet; }
            }
        }
    }

    return false;
};

Which you would call directly on the data object, passing in the key/value you're looking for:

data.findKey({ id: 3 });

Note that this function allows you to find an object based on any key:

data.findKey({ name: 'Template 0' });

See example → (open console to view result)

Solution 2

Not the best of the and final solution. But can get you a start for what you are looking...

var data = [{id: 0, name: 'Template 0', subComponents:[
        {id: 1, name: 'Template 1', subItems:[
            {id: 2, name: 'Template 2', subComponents:[{id: 3, name: 'Template 3'}], subItems: [{id: 4, name: 'Template 4'}]}
        ]}
    ]}
];


function returnObject(data,key,parent){
    for(var v in data){

        var d = data[v];
        if(d==key){
            return parent[0];
        }
        if(d instanceof Object){
            return returnObject(d,key,data);
        };

    }
}

function returnObjectWrapper(datavar,key){
    return returnObject(datavar,key.id)
}

returnObjectWrapper(data,{id:3})

Solution 3

Please see my solution below or http://jsfiddle.net/8Y6zq/:

var findByKey = function (obj, key) {
var j, key = key || '', obj = obj || {}, keys = key.split("."), 
    sObj = [], ssObj = [], isSelector = !!(keys.length > 0);

    var findKey = function (obj, key) {
        var k;
        for (k in obj) {
            if (k === key) {
                sObj.push(obj[k]);
            } else if (typeof obj[k] == 'object') {
                findKey(obj[k], key);
            }
        }
    };

    if (isSelector) {
        var nKey = keys.shift();
        findKey(obj, nKey);

        while (keys.length > 0) {
            nKey = keys.shift();

            if (sObj.length > 0) {
                ssObj = sObj.slice(0), sObj = [];
                for (j in ssObj) {
                    findKey(ssObj[j], nKey);
                }
            }
        }
    } else {
        findKey(obj, key);
    }

    // return occurrences of key in array
    return (sObj.length === 1) ? sObj.pop() : sObj;
};

var data = [
    {id: 0, name: 'Template 0', subComponents: [
            {id: 1, name: 'Template 1', subItems: [
            {id: 2, name: 'Template 2', subComponents: [
                {id: 3, name: 'Template 3'}
            ], subItems: [
                {id: 4, name: 'Template 4'}
            ]}
        ]}
    ]},
    {subComponents:{
        comp1:'comp1 value',
        comp2:'comp2 value',
    }}
];

alert(JSON.stringify(findByKey(data, 'subComponents')));
alert(JSON.stringify(findByKey(data, 'subComponents.comp1')));
alert(JSON.stringify(findByKey(data, 'subComponents.comp2')));

In this implementation we can use search by KEY or SELECTOR (eg. "<paren_key>.<child_key_1>.<child_key_2>. ... <child_key_N>")

Share:
16,781
Devin McQueeney
Author by

Devin McQueeney

Web Application Architect &amp; Full Stack Developer

Updated on July 05, 2022

Comments

  • Devin McQueeney
    Devin McQueeney almost 2 years

    I've got some nested object data and I want to search it and return the matching object based on the id.

    var data = [{id: 0, name: 'Template 0', subComponents:[
            {id: 1, name: 'Template 1', subItems:[
                {id: 2, name: 'Template 2', subComponents:[{id: 3, name: 'Template 3'}], subItems: [{id: 4, name: 'Template 4'}]}
            ]}
        ]}
    ];
    

    So I want to do something like this

    getObjectByKeyValue({id: 3}) 
    

    and have it return

    {id: 3, name: 'Template 3'}
    

    It's sort of got to be done generically because I have subItems, AND subComponents which could each have children.

    I tried this using Prototype 1.7 and no luck - I think this just searches an array, and not a tree with it's sub nodes:

    data.find(function(s){return s.id == 4;})
    

    Thanks in advance!!!!!!

  • Devin McQueeney
    Devin McQueeney about 13 years
    I like this solution a lot better but this doesn't seem to work when the prototype library is in the mix - any clue how to make it work with the prototype library?
  • mVChr
    mVChr about 13 years
    I switched the fiddle to include the Prototype 1.7 library and it still works for me so I'm not sure what issue you're having.
  • Anupam
    Anupam about 9 years
    Nice solution. What if I want to search based on multiple keys. Added a question here for the same
  • AlphaG33k
    AlphaG33k over 8 years
    Bad bad bad! Extension of the native object prototype!
  • jsleuth
    jsleuth almost 8 years
    If you don't want to extend the object prototype this is easily modifiable to be a standalone method-- just pass in the data to search and replace references to this with the passed argument name.