Nested Recursive Object Loop
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};
}
}
}
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"