4

How can i recursively search a nested object to find the PATH to an object reference I'm providing?

My original object looks like:

a = {
 b: [
  { children: [...more objects] },
  { children: [] }
  etc..
 ],
 c: [
  { children: [...more objects] },
  { children: [] }
  etc..
 ]
}

I would like to call a function findDeepAndStorePath(a, obj) which would find the object reference and store the path to it in an array of indexes such as: ['b', 0, 1, 2].

Dragos Ionescu
  • 1,163
  • 1
  • 9
  • 12

3 Answers3

12

function findPath(a, obj) {
    for(var key in obj) {                                         // for each key in the object obj
        if(obj.hasOwnProperty(key)) {                             // if it's an owned key
            if(a === obj[key]) return key;                        // if the item beign searched is at this key then return this key as the path
            else if(obj[key] && typeof obj[key] === "object") {   // otherwise if the item at this key is also an object
                var path = findPath(a, obj[key]);                 // search for the item a in that object
                if(path) return key + "." + path;                 // if found then the path is this key followed by the result of the search
            }
        }
    }
}

var obj = {
  "a": [1, 2, {"o": 5}, 7],
  "b": [0, [{"bb": [0, "str"]}]]
};

console.log(findPath(5, obj));
console.log(findPath("str", obj).split("."));                     // if you want to get the path as an array you can simply split the result of findPath
ibrahim mahrir
  • 31,174
  • 5
  • 48
  • 73
3

You could use Object.keys and check the values. If found, then the actual path is returned and the iteration stops. If not, all possible pathes are checked.

This proposal respects numeric keys off arrays.

function findPath(a, obj) {
    function iter(o, p) {
        return Object.keys(o).some(function (k) {
            result = p.concat(Array.isArray(o) ? +k : k);
            return o[k] === a || o[k] && typeof o[k] === 'object' && iter(o[k], result);
        });
    }
    var result;
    return iter(obj, []) && result || undefined;
}

var obj = { a: [1, 2, { o: 5 }, 7], b: [0, [{ bb: [0, "str"] }]] };

console.log(findPath(5, obj));     // ["a", 2, "o"]
console.log(findPath("str", obj)); // ["b", 1, 0, "bb", 1]
console.log(findPath(42, obj));    // undefined
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

Don't reinvent the wheel. We use object-scan for all our data processing needs. It's powerful once you wrap your head around it. Here is how you'd use it for your question

// const objectScan = require('object-scan');

const findPath = (id, data) => objectScan(['**'], {
  abort: true,
  rtn: 'key',
  filterFn: ({ value }) => value === id
})(data);

const obj = { a: [1, 2, { o: 5 }, 7], b: [0, [{ bb: [0, 'str'] }]] };

console.log(findPath(5, obj));
// => [ 'a', 2, 'o' ]
console.log(findPath('str', obj));
// => [ 'b', 1, 0, 'bb', 1 ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan@13.8.0"></script>

Disclaimer: I'm the author of object-scan

vincent
  • 1,953
  • 3
  • 18
  • 24