Reputation: 123
Consider the following tree.
It's represented by the following data structure.
const Node = (name, value, children) => ({ name, value, children });
const tree = Node("A", 4, [
Node("B", 7, [
Node("C", 9, [])
]),
Node("D", 11, [
Node("E", 9, [])
]),
Node("F", 55, []),
Node("G", 65, [
Node("H", 21, []),
Node("I", 23, [])
])
]);
Now, I want to get an array of all the values in the tree greater than a certain value. Here's what I tried.
const Node = (name, value, children) => ({ name, value, children });
const tree = Node("A", 4, [
Node("B", 7, [
Node("C", 9, [])
]),
Node("D", 11, [
Node("E", 9, [])
]),
Node("F", 55, []),
Node("G", 65, [
Node("H", 21, []),
Node("I", 23, [])
])
]);
const result = findHigherInNode(tree, 20);
console.log(result);
function findHigherInNode(node, num) {
console.log(node.name);
const isGreater = [];
if (node.value > num) {
isGreater.push(node.value);
}
for (let i = 0; i < node.children.length; i++) {
findHigherInNode(node.children[i], num);
}
return isGreater;
}
Note that although I'm traversing the entire tree, yet I'm not getting the correct result.
However, if I move the recursion into a local recurse
function while keeping the isGreater
array in the same scope then it works.
const Node = (name, value, children) => ({ name, value, children });
const tree = Node("A", 4, [
Node("B", 7, [
Node("C", 9, [])
]),
Node("D", 11, [
Node("E", 9, [])
]),
Node("F", 55, []),
Node("G", 65, [
Node("H", 21, []),
Node("I", 23, [])
])
]);
const result = findHigherInNode(tree, 20);
console.log(result);
function findHigherInNode(node, num) {
const isGreater = [];
function recurse(node, num) {
if (node.value > num) {
isGreater.push(node.value);
}
for (let i = 0; i < node.children.length; i++) {
recurse(node.children[i], num);
}
}
recurse(node, num);
return isGreater;
}
Why does my first code snippet produce the wrong result? Why does my second code snippet produce the right result? The only difference between them is that the isGreater
array is lifted to a higher scope in the second code snippet.
Upvotes: 1
Views: 50
Reputation: 135396
Why does my first code snippet produce the wrong result?
Because isGreater
is a new binding, initialized to []
each time findHigherInNode
is called
Why does my second code snippet produce the right result?
Because isGreater
is initialized once after findHigherInNode
is called, and recurse
does not create a new binding for each subsequent call
Why am I experiencing this kind of problem?
Because you are not using generators! Generators make this kind of problem disappear -
function* inorder (t)
{ yield t.value
for (const c of t.children)
yield *inorder(c)
}
const tree =
{name: 'A',value: 21,children: [{name: 'B', value: 7,children: [{name: 'C', value: 9, children: []}]},{name: 'D', value: 11,children: [{name: 'E', value: 9, children: []}]},{name: 'F', value: 55, children: []},{name: 'G', value: 65,children: [{name: 'H', value: 21, children: []},{name: 'I', value: 33, children: []}]}]}
for (const v of inorder(tree))
if (v > 20)
console.log(v)
21
55
65
21
33
However, it would probably be smarter to yield the entire node, allowing the caller of the generator to choose which data is relevant to select from the traversal
function* inorder (t)
{ yield t // <- yield entire node
for (const c of t.children)
yield *inorder(c)
}
const tree =
{name: 'A',value: 21,children: [{name: 'B', value: 7,children: [{name: 'C', value: 9, children: []}]},{name: 'D', value: 11,children: [{name: 'E', value: 9, children: []}]},{name: 'F', value: 55, children: []},{name: 'G', value: 65,children: [{name: 'H', value: 21, children: []},{name: 'I', value: 33, children: []}]}]}
for (const node of inorder(tree))
if (node.value > 20) // <- entire node accessible here
console.log(node.name, node.value)
A 21
F 55
G 65
H 21
I 33
But is it possible without generators?
Yep.
function inorder (t)
{
return [t, ...t.children.flatMap(inorder)]
}
const tree =
{name: 'A',value: 21,children: [{name: 'B', value: 7,children: [{name: 'C', value: 9, children: []}]},{name: 'D', value: 11,children: [{name: 'E', value: 9, children: []}]},{name: 'F', value: 55, children: []},{name: 'G', value: 65,children: [{name: 'H', value: 21, children: []},{name: 'I', value: 33, children: []}]}]}
for (const node of inorder(tree))
if (node.value > 20)
console.log(node.name, node.value)
A 21
F 55
G 65
H 21
I 33
Upvotes: 3