Reputation: 1128
I need to recursively loop through an array of objects and each object object has a property label
which needs to be modified to include the children
count.
Take a look at this example:
const nodes = [{
value: 'World',
label: 'World',
children: [{
label: 'Europe',
value: 'Europe',
children: [
{
label: 'Albania',
value: 'AL'
},
{
label: 'BeNeLux',
value: 'BeNeLux',
children: [
{
label: 'The Netherlands',
value: 'NL'
},
{
label: 'Belgium',
value: 'BE'
},
{
label: 'Luxembourg',
value: 'LU'
}
]
}
]
}]
}]
The expected output would be:
const expectedOutput = [{
value: 'World',
label: 'World (4)',
children: [{
label: 'Europe (4)',
value: 'Europe',
children: [
{
label: 'Albania',
value: 'AL'
},
{
label: 'BeNeLux (3)',
value: 'BeNeLux',
children: [
{
label: 'The Netherlands',
value: 'NL'
},
{
label: 'Belgium',
value: 'BE'
},
{
label: 'Luxembourg',
value: 'LU'
}
]
}
]
}]
}]
This is what I have working now, but it's not working correctly because as mentioned in the expectedOutput
above, Europe
's label would be Europe (4)
and my version counts Europe (2)
because it's ignoring the children inside Europe.
export const getSortedNodesWithChildrenCountLabel = nodes => {
return nodes
.reduce(function f (output, node) {
if (node?.children) {
node.label += ` (${node.children.length})`
node.children = node.children
.reduce(f, [])
}
output.push(node)
return output
}, [])
}
Upvotes: 4
Views: 1371
Reputation: 386680
You could take a recursive approach and get the count from the children and update label
.
This approach mutates the data.
function update(nodes) {
return nodes.reduce((count, node) => {
if (node.children) {
var subcount = update(node.children);
node.label += ` (${subcount})`;
return count + subcount;
}
return count + 1;
}, 0);
}
const nodes = [{ value: 'World', label: 'World', children: [{ label: 'Europe', value: 'Europe', children: [{ label: 'Albania', value: 'AL' }, { label: 'BeNeLux', value: 'BeNeLux', children: [{ label: 'The Netherlands', value: 'NL' }, { label: 'Belgium', value: 'BE' }, { label: 'Luxembourg', value: 'LU' }] }] }] }];
update(nodes);
console.log(nodes);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A non mutating version which gets the count with an object to the outer call.
function fn(count = { count: 0 }) {
return function ({ value, label, children }) {
var sub = { count: 0 };
if (children) {
children = children.map(fn(sub)),
label += ` (${sub.count})`;
count.count += sub.count;
return { value, label, children };
}
count.count++;
return { value, label };
};
}
const
nodes = [{ value: 'World', label: 'World', children: [{ label: 'Europe', value: 'Europe', children: [{ label: 'Albania', value: 'AL' }, { label: 'BeNeLux', value: 'BeNeLux', children: [{ label: 'The Netherlands', value: 'NL' }, { label: 'Belgium', value: 'BE' }, { label: 'Luxembourg', value: 'LU' }] }] }] }],
withCount = nodes.map(fn());
console.log(withCount);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation: 7675
You can decompose children counting into a separate recursive function:
const nodes = [{
value: 'World',
label: 'World',
children: [{
label: 'Europe',
value: 'Europe',
children: [
{
label: 'Albania',
value: 'AL'
},
{
label: 'BeNeLux',
value: 'BeNeLux',
children: [
{
label: 'The Netherlands',
value: 'NL'
},
{
label: 'Belgium',
value: 'BE'
},
{
label: 'Luxembourg',
value: 'LU'
}
]
}
]
}]
}]
const getChildrenCount = (node, count = 0) => {
if (!node.children) {
return 1
}
for (const child of node.children) {
count += getChildrenCount(child)
}
return count;
}
const getSortedNodesWithChildrenCountLabel = nodes => {
return nodes
.reduce(function f (output, node) {
if (node.children) {
node.label += ` (${getChildrenCount(node)})`
node.children = node.children
.reduce(f, [])
}
output.push(node)
return output
}, [])
}
console.log(getSortedNodesWithChildrenCountLabel(nodes))
Upvotes: 2