looping through an object (tree) recursively

JavascriptJqueryTree

Javascript Problem Overview


Is there a way (in jQuery or JavaScript) to loop through each object and it's children and grandchildren and so on?

If so... can I also read their name?

Example:

foo :{
  bar:'',
  child:{
    grand:{
      greatgrand: {
        //and so on
      }
    }
  }
}

so the loop should do something like this...

loop start
   if(nameof == 'child'){
     //do something
   }
   if(nameof == 'bar'){
     //do something
   }
   if(nameof =='grand'){
     //do something
   }
loop end

Javascript Solutions


Solution 1 - Javascript

You're looking for the for...in loop:

for (var key in foo)
{
    if (key == "child")
        // do something...
} 

Be aware that for...in loops will iterate over any enumerable properties, including those that are added to the prototype of an object. To avoid acting on these properties, you can use the hasOwnProperty method to check to see if the property belongs only to that object:

for (var key in foo)
{
    if (!foo.hasOwnProperty(key))
        continue;       // skip this property
    if (key == "child")
        // do something...
}

Performing the loop recursively can be as simple as writing a recursive function:

// This function handles arrays and objects
function eachRecursive(obj)
{
    for (var k in obj)
    {
        if (typeof obj[k] == "object" && obj[k] !== null)
            eachRecursive(obj[k]);
        else
            // do something... 
    }
}

Solution 2 - Javascript

You can have a recursive function with a parse function built within it.

function parseObjectProperties (obj, parse) {
  for (var k in obj) {
    if (typeof obj[k] === 'object' && obj[k] !== null) {
      parseObjectProperties(obj[k], parse)
    } else if (obj.hasOwnProperty(k)) {
      parse(k, obj[k])
    }
  }
}

I use the foo object of the OP, here how it works

var foo = {
  bar:'a',
  child:{
    b: 'b',
    grand:{
      greatgrand: {
        c:'c'
      }
    }
  }
}

// use this recursive function with a parse funciton
function parseObjectProperties (obj, parse) {
  for (var k in obj) {
    if (typeof obj[k] === 'object' && obj[k] !== null) {
      parseObjectProperties(obj[k], parse)
    } else if (obj.hasOwnProperty(k)) {
      parse(k, obj[k])
    }
  }
}
//***

// then apply to the property the task you want, in this case just console
parseObjectProperties(foo, function(k, prop) {
  console.log(k + ': ' + prop)
})

Solution 3 - Javascript

If you want to get back a tree of relationships you can use Object.keys recursively.

function paths(item) {
  function iter(r, p) {
    var keys = Object.keys(r);
    if (keys.length) {
      return keys.forEach(x => iter(r[x], p.concat(x)));
    }
    result.push(p);
  }
  var result = [];
  iter(item, []);
  return result;
}

var data = {
  foo: {
    bar: '',
    child: {
      grand: {
        greatgrand: {}
      }
    }
  }
};

console.log(paths(data));

This can be extended to search for values within an object structure that match a function:

function objectSearch(rootItem, matcher) {
  const visited = [];
  const paths = [];
  function iterate(item, path) {
    if (visited.includes(item)) {
      return;
    }
    visited.push(item);
    if (typeof item === "object" && item !== null) {
      var keys = Object.keys(item);
      if (keys.length) {
        return keys.forEach(key => iterate(item[key], path.concat(key)));
      }
    }
    if (matcher(item)) {
      paths.push(path);
    }
  }
  iterate(rootItem, []);
  return paths;
}
function searchForNaNs(rootItem) {
  return objectSearch(rootItem, (v) => Object.is(NaN, v));
}
var banana = {
  foo: {
    bar: "",
    child: {
      grand: {
        greatgrand: {},
        nanan: "NaN",
        nan: NaN,
      },
    },
  },
};
console.log("There's a NaN at", searchForNaNs(banana)[0].join("."), "in this object:", banana);

Solution 4 - Javascript

Consider using object-scan. It's powerful for data processing once you wrap your head around it.

One great thing is that the items are traversed in "delete safe" order. So if you delete one, it won't mess up the loop. And you have access to lots of other properties like parents etc.

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

const obj = { foo: { bar: '', child: { grand: { greatgrand: { /* and so on */ } } } } };

objectScan(['**'], {
  filterFn: ({ property }) => {
    console.log(property);
  }
})(obj);
// => greatgrand
// => grand
// => child
// => bar
// => foo

<script src="https://bundle.run/[email protected]"></script>

.as-console-wrapper {max-height: 100% !important; top: 0}

Disclaimer: I'm the author of object-scan

Solution 5 - Javascript

I would recommend using sindresorhus's map-obj & filter-obj utilities ...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionValView Question on Stackoverflow
Solution 1 - JavascriptAndy EView Answer on Stackoverflow
Solution 2 - JavascriptJoão Pimentel FerreiraView Answer on Stackoverflow
Solution 3 - JavascriptRickView Answer on Stackoverflow
Solution 4 - JavascriptvincentView Answer on Stackoverflow
Solution 5 - JavascriptAli SharifView Answer on Stackoverflow