I have an infinite tree:
const Data = [
{
id: '1',
name: 'hello',
children: [
{
id: '2',
name: 'world',
children: [
{
id: '3',
name: 'world',
children: [],
},
{
id: '4',
name: 'world',
children: [],
},
],
},
{
id: '5',
name: 'world',
children: [],
},
],
},
];
What I want to do is get the id and name of the path that leads to "world" and push it in to an array.
For example: the first path would be:
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
]
second:
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
{ id: '3', name: 'world' },
]
And then push those arrays into another array.
So my result would look like this:
const result = [
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
],
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
{ id: '3', name: 'world' },
],
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
{ id: '4', name: 'world' },
],
[
{ id: '1', name: 'hello' },
{ id: '5', name: 'world' },
],
];
I have a recursive function:
const findPath = (input="world", data, visitedStack, dataStack) => {
return data.map((node) => {
visitedStack.push({ id: node.id, name: node.name });
if (node.name.toLowerCase().includes(input.toLowerCase())) {
dataStack.push([...visitedStack]);
}
return findPath(
input,
node.children,
visitedStack,
dataStack
);
});
};
But this is adding on all the paths it has visited, so the last array that is pushed into dataStack will look like this:
[
{ id: '1', name: 'hello' },
{ id: '2', name: 'world' },
{ id: '3', name: 'world' },
{ id: '4', name: 'world' },
{ id: '5', name: 'world' },
]
Not sure how to fix this. Or is this an incorrect approach?
The problem is that your visitedStack
keeps growing, as you are eventually pushing all nodes unto it. Be aware that all recursive executions of your function get the same visitedStack
to work with. So pushing [...visitedStack]
is not going to push a path, but all nodes that had been visited before, which after a while do not represent a path any more.
If we stick with your function, then just make sure you don't push on visited
permanently, but create a copy of that stack with the extra node, which will remain in the deeper recursion, but will not contaminate the whole rest of the execution. This way that extra node will not be there in the other, sibling paths:
const findPath = (input="world", data, visitedStack, dataStack) => {
return data.map((node) => {
let newStack = visitedStack.concat({ id: node.id, name: node.name });
if (node.name.toLowerCase().includes(input.toLowerCase())) {
dataStack.push(newStack);
}
return findPath(
input,
node.children,
newStack,
dataStack
);
});
};
Call as:
let result = [];
findPath("world", data, [], result);
console.log(result);
I would however also address the following:
It is a bit odd that findPath
does not return the result, but that the caller needs to provide the array in which the resulting paths should be collected. So I would suggest a function that returns the new array, not requiring the caller to pass that array as argument.
It is not useful to have a default value for a parameter, when other parameters following it, do not have a default value. Because, that means you anyway have to provide values for those other parameters, including the one that could have had a default value.
The paths that are returned still contain multiple references to the same objects. You do copy the objects into new objects, but as that new object sits in visitedStack
, it will be reused when pushed potentially several times for deeper paths. So I would suggest making the object copies at the very last moment -- when the path is pushing on the result array.
Instead of repeatedly converting the input to lower case, do this only once.
Here is how you could write it:
function findPath(data, input="world") {
const result = [];
input = input.toLowerCase();
function recur(data, visitedStack) {
for (const node of data) {
const newStack = visitedStack.concat(node);
if (node.name.toLowerCase().includes(input)) {
result.push(newStack.map(o => ({id: o.id, name:o.name})));
}
recur(node.children, newStack);
}
}
recur(data, []);
return result;
}
const data = [{id: '1',name: 'hello',children: [{id: '2',name: 'world',children: [{id: '3',name: 'world',children: [],},{id: '4',name: 'world',children: [],},],},{id: '5',name: 'world',children: [],},],},];
const result = findPath(data);
console.log(result);