2

I've got a data structure that looks something like this:

let tree = {
  id: 1,
  name: "Some Name",
  children: [
    {
      id: 2,
      name: "Child 1",
      children: [...more nested objects...]
    }
  ]
};

I've written a recursive function to find a given object within that tree, but I now need to also return the path through the tree to the object that is returned. I'm trying to figure out how to modify my search function to do this.

Search function:

_findInTree = (id, tree) => {
    let result;
    if (tree.id === id) {
      result = tree;
    } else {
      for (let child of tree.children) {
        if (child.id === id) { result = child; }
        result = this._findInTree(id, child);
        if (result) { break; }
      }
    }
    return result;
  }

3 Answers 3

5

You'll need the array index, so you can either track it outside the for-of and then use it on the path, or use Array#some instead (or use a boring old for).

Here's tracking the index outside the for-of — I also added an else I think was probably pretty important: :-)

_findInTree = (id, tree, path = "") => {
    let result;
    let index;
    let rv;
    if (tree.id === id) {
        result = tree;
    } else {
        index = 0;
        for (let child of tree.children) {
            if (child.id === id) {
                result = child;
                break;
            }
            rv = this._findInTree(id, child, path + "[" + index + "]");
            if (rv != null) {
                return rv;
            }
            ++index;
        }
    }
    return { result, path };
};

Obviously, adjust the format of path as you see fit. (Doesn't have to be a string, for instance, could be an array.)

Here's the some solution:

_findInTree = (id, tree, path = "") => {
    let result;
    let rv = null;
    if (tree.id === id) {
        result = tree;
    } else {
        tree.children.some((child, index) => {
            if (child.id === id) {
                result = child;
                return true;
            }
            rv = this._findInTree(id, child, path + "[" + index + "]");
            if (rv) {
                return true;
            }
        });
    }
    return rv || { result, path };
};
Sign up to request clarification or add additional context in comments.

2 Comments

I changed path to an array, and that worked great. Thanks!
@KevinWhitaker: Good deal. See my edit to your edit, ES6 lets us do just return { result, path }; instead of return { result: result, path: path }; (the result [no pun!] is identical).
1

So T.J. Crowders position ended up having a bug around recording the path, and I ended up tweaking the solution to get the following, which works excellently.

  _findInTree(id, tree) {
    if (tree.id === id) {
      let path = [tree.name];
      return {result: tree, path};
    } else {
      for (let child of tree.children) {
        let tmp = this._findInTree(id, child);
        if (!_.isEmpty(tmp)) {
          tmp.path.unshift(tree.name);
          return tmp;
        }
      }
      return {};
    }
  }

3 Comments

It is, however, fundamentally the same solution. Not sure what bug you're talking about, either, unless it's the one that came about as a result of your edit to the answer. :-) (Which was you fixing the fact I'd kind of forgotten to return the path at all...) Answers to questions like this are largely meant to point the way, not get the code spot-perfect.
Absolutely! Your answer got me 90% of the way :)
I'm very confused about the above code. I actually tried to run the code myself, and my suspicions were confirmed: neither 'this' nor '_' refer to anything; they're undefined.
0

As For me, I need to change Kevin Whitaker code to this one

_findInTree(id, tree) {
    if (tree.id === id) {
      let path = [tree.name];
      return {result: tree, path};
    } else if (tree.children) { //THIS IS THE CHANGES THAT I NEED
      for (let child of tree.children) {
        let tmp = this._findInTree(id, child);
        if (!_.isEmpty(tmp)) {
          tmp.path.unshift(tree.name);
          return tmp;
        }
      }
      return {};
    }
  }

Comments

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.