0

I don't think "nested values" is the right term, but here's what I'm trying to do:

Let's say I have an object that looks like this:

{
    title: 'Foo',
    content: {
        top: 'Bar',
        bottom: 'Baz'
    }
}

And I want to check if either title or content.top or content.bottom contains a certain string.

I've found that I can loop through an object keys with something like this:

for (var property in object) {
    if (object.hasOwnProperty(property)) {
        // do stuff
    }
}

But what if the key is an object in itself and contains other keys? What if those keys are also objects with different keys? So basically, is there a way to search the entire object in a "deep" way, so that it searches all values no matter how deep the values are "nested"?

6
  • Do you want to loop through every value? Or do you know exactly which one you need? If you know the exact property, you can use dot notation, eg myObject.content.top Commented Nov 23, 2017 at 19:21
  • Yeah I do want to loop through all the values, because I'm working with different objects with different values (and they're many, so manually adding them is unpractical) Commented Nov 23, 2017 at 19:23
  • What do you want returned? And only find first match? Commented Nov 23, 2017 at 19:23
  • I have an array with a lot of objects, and would like another array returned, with all the objects that contain a certain string. Commented Nov 23, 2017 at 19:25
  • Lots of solutions if you do a site search - javascript object deep search Commented Nov 23, 2017 at 19:26

1 Answer 1

5

Nested objects form a recursive data structure called a tree, and a tree is easily browsable with recursive functions. Recursive functions are functions that call themselves, like the following browse function :

var tree = {
  a: "azerty",
  child: {
    q: "qwerty"
  }
};

browse(tree);

function browse (tree) {
  for (var k in tree) {
    if (isTree(tree[k])) {
      browse(tree[k]);
    } else {
      console.log(k, tree[k]);
    }
  }
}

function isTree (x) {
  return Object.prototype.toString.call(x) === "[object Object]";
}

However, this function is designed to perform the same task over and over. A more generic approach would be to outsource operations applied to each leaf :

var tree = {
  a: 'azerty',
  child: {
    q: 'qwerty'
  }
};

browse(tree, log);
browse(tree, searchQ);

function browse (tree, operation) {
  for (var k in tree) {
    if (isTree(tree[k])) {
      browse(tree[k], operation);
    } else {
      operation(k, tree[k]);
    }
  }
}

function log (label, leaf) {
  console.log("logged", label, leaf);
}

function searchQ (label, leaf) {
  if (leaf.indexOf('q') !== -1) {
    console.log("found", label, leaf);
  }
}

function isTree (x) {
  return Object.prototype.toString.call(x) === "[object Object]";
}

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the update! One question though: Using this method to search through an array of "trees", how would I return the "top" tree, rather than a nested tree? If you know what I mean..
Well, if I run the browse function the second time, the tree parameter becomes tree.child, rather than tree, right? I want to be able to return tree when a match is found. But I think I can solve this by adding a second argument to the function with which I can pass the original tree.
Basically in the searchQ function, I want it to log the "tree" variable, instead of the actual tree that contains the value.. This is because I'm looping through an array of objects and I want to return the object that contains the value, not the tree. Here you can see what I mean: jsfiddle.net/g9nnr9xu/1. It now logs {top: "test2", bottom: "test3"} but I want it to log the tree that I pass in the forEach loop. Sorry if I'm explaining this badly.. But I can fix it with adding another parameter, so now I have it working :)
@Leon Ok I understand now. Yes you need an additional parameter. Another option would be to add a "parent" property to every child in order to be able to get to the root at any level. This is how the DOM works actually, see the parentNode property : developer.mozilla.org/en-US/docs/Web/API/Node/parentNode. Sorry for posting the same comment multiple times, I had to fix some inconsistencies :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.