Skip to content Skip to sidebar Skip to footer

Nested Recursive Object Loop

I have arrays of objects that can also have arrays of their own. My main goal is to find an object with a given id in the whole tree and get readmap to that element by displaying

Solution 1:

You would need to pass down another property which handles the path. Start by defining path as an empty array. And since you only care about the name, you can push the name into this array everytime you find a node that has children.

Then you just keep passing the updated array to your recursive function. See my working example below:

(I updated your function to return an object which contains both the result and path)

functionfindByName(name, array, path = []) {
  for (const node of array) {
    if (node.name === name) return {result: node, path};
    if (node.children) {
      path.push(node.name) // We update the path with the current node name that has childrenconst child = findByName(name, node.children, path );
      if (child) return { result: child, path};
    }
  }
}

Demo: https://jsitor.com/VnktoLq49

Solution 2:

You could add the path for every leve in the calling function without handing over the path.

constfindByName = (array, name) => {
        for (const node of array) {
            if (node.name === name) return { ...node, path: [] };
            if (node.children) {
                const child = findByName(node.children, name);
                if (child) return { ...child, path: [node.name, ...child.path] };
            }
        }
    },
    data = [{ id: '0', name: "Boys", children: [{ name: "Soldiers", children: [{ name: "Bravo", children: [{ name: "Tom" }, { name: "Andrew" }] }] }, { name: "Runners", children: [{ name: "Team B", children: [{ name: "Mark" }, { name: "David" }] }] }] }];

console.log(findByName(data, 'Tom'));
.as-console-wrapper { max-height: 100%!important; top: 0; }

Solution 3:

I like generators for this kind or problem because it allows you to select one, many, or all results. Additionally generators give control to the caller, allowing you to stop searching whenever you are satisfied with the result. This can be accomplished with a single function -

function* select(a = [], query = Boolean, path = [])
{ for (const t of a)
  { if (query(t)) yield { ...t, path }
    yield *select(t.children, query, [...path, t.name])
  }
}

const data =
  [{ id: '0', name: "Boys", children: [{ name: "Soldiers", children: [{ name: "Bravo", children: [{ name: "Tom" }, { name: "Andrew" }] }] }, { name: "Runners", children: [{ name: "Team B", children: [{ name: "Mark" }, { name: "David" }] }] }] }]

// select "Tom" OR "Mark"for (const r ofselect(data, v => v.name == 'Tom' || v.name == "Mark"))
  console.log("found:", r)
  
found: {
  "name": "Tom",
  "path": [
    "Boys",
    "Soldiers",
    "Bravo"
  ]
}
found: {
  "name": "Mark",
  "path": [
    "Boys",
    "Runners",
    "Team B"
  ]
}

If you want only the first result, we can use return or break, and searching stops immediately, potentially saving many wasted computations -

functionfirst (it)
{ for (const x of it)
    return x              // <- return and stop searching
}

first(select(data, v => v.name == "Andrew"))
{
  "name": "Andrew",
  "path": [
    "Boys",
    "Soldiers",
    "Bravo"
  ]
}

If you want all of the results, we can use Array.from. Because select is flexible, it allows us to do all sorts of useful queries -

Array.from(select(data, v => !v.children), r => r.name)
[
  "Tom",
  "Andrew",
  "Mark",
  "David"
]

Post a Comment for "Nested Recursive Object Loop"