2

I have this JSON payload, where each object contains an ID, name and array of children. Here I need to get the IDs of all the elements, including the root and all the nested children.

{
  "_id": "-1",
  "_name": "root",
  "_children": [
    {
      "_id": "1",
      "_name": "Child 1",
      "_children": [
        {
          "_id": "1-1",
          "_name": "Child 1-1",
          "_children": [
            {
              "_id": "1-1-1",
              "_name": "Child 1-1-1",
              "_children": [

              ]
            }
          ]
        },
        {
          "_id": "1-2",
          "_name": "Child 1-2",
          "_children": [

          ]
        },
        {
          "_id": "1-3",
          "_name": "Child 1-3",
          "_children": [

          ]
        }
      ]
    },
    {
      "_id": "2",
      "_name": "Child 2",
      "_children": [
        {
          "_id": "2-2",
          "_name": "Child 2-2",
          "_children": [

          ]
        }
      ]
    }
  ]
}

How can I loop through this to get the ID values of all children and the root?

This is what I had tried using a nested function, but it is not working.

getNestedChildren(arr) {
  var out = []
    for(var i in arr[0].children) {
      out.push(arr[i].id);
        if(arr[i].children && arr[i].children.size() > 0) {
            var children = this.getNestedChildren(arr[i].children)
        }
    }
2
  • Can you show expected output? Commented Feb 6, 2020 at 8:39
  • @Mridul [-1, 1, 1-1, 1-1-1, 1-2, 1-3, 2, 2-2] Commented Feb 6, 2020 at 9:01

3 Answers 3

4
+50

You can use recursion and modify a "result array" by reference.

For example, if your nested object is stored in a variable data, then:

function addNestedChildrenToArray(obj, resultArray) {
    resultArray.push(obj._id);
    obj._children.forEach(child => addNestedChildrenToArray(child, resultArray));
}

const resultArray = [];
addNestedChildrenToArray(data, resultArray);
// resultArray now contains the results
console.log(resultArray);

You can test it here: https://es6console.com/k6ehwu5p/

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

4 Comments

This is definitely the natural way to solve this problem. For convenience, I'd suggest wrapping this all in a function that can be called with a tree to return an array, so the caller doesn't have to create the empty array themselves.
@samdouble What we should change to your code to be able to start from a specfied _id and get only the results under that node? Like passing one more parameter to your function with the _id and only iterate from that id and below in the tree.
@WilliamBird I haven't tried it but (sorry, I don't know how to format my code in the comment): function addNestedChildrenToArray(obj, resultArray, searchId, include=false) { const shouldInclude = include || obj._id === searchId; if (shouldInclude) { resultArray.push(obj._id); } obj._children.forEach(child => addNestedChildrenToArray(child, resultArray, searchId, shouldInclude)); } const resultArray = []; addNestedChildrenToArray(data, resultArray, SEARCHID); // resultArray now contains the results console.log(resultArray);
@samdouble Yes it works, thank you, here is a demo link: es6console.com/kg28pqkw
1

You can flatten a tree and then simply get ids. See working example here

const tree = {
  "_id": "-1",
  "_name": "root",
  "_children": [
    // ...
  ]
}

function flattenTree(tree) {
  if (!tree) {
      return [];
  }

  if (tree._children) {
      const result = tree._children.reduce((prev, current) => prev.concat(flattenTree(current)), [tree]);
      return result;
  } else {
      return [tree];
  }
}

const plain = flattenTree(tree);
const ids = plain.map(value => value._id);

Comments

1

Here's a solution which works essentially the same way as @samdouble's answer; push the current node's ID to a result array, then recurse on the node's children.

The result array has to be created before calling the recursive function, so for convenience, that can be wrapped in an outer function; this saves the caller having to create an empty array to pass to the function.

interface TreeNode {
    _id: string;
    _name: string;
    _children: TreeNode[];
}

function getIds(data: TreeNode): string[] {
    const result: string[] = [];
    function helper(node: TreeNode): void {
        result.push(node._id);
        node._children.forEach(helper);
    }
    helper(data);
    return result;
}

Playground Link

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.