Reputation: 323
Every parent element should contain total count of all subcategories. It will be very good if solution will be using only Array object methods and no using of while
loops.
This is the example of base structure:
const base = [
{ id: 1, count: 2, parentId: null },
{ id: 2, count: 4, parentId: 1 },
{ id: 3, count: 0, parentId: 2 },
{ id: 6, count: 8, parentId: 3 },
{ id: 7, count: 8, parentId: 3 },
{ id: 8, count: 2, parentId: 7 },
{ id: 4, count: 2, parentId: null },
{ id: 5, count: 1, parentId: 4 },
];
This is how it should be looking like:
const expected = [
{ id: 1, count: 2, total: 24, parentId: null },
{ id: 2, count: 4, total: 22, parentId: 1 },
{ id: 3, count: 0, total: 18, parentId: 2 },
{ id: 6, count: 8, total: 8, parentId: 3 },
{ id: 7, count: 8, total: 10, parentId: 3 },
{ id: 8, count: 2, total: 2, parentId: 7 },
{ id: 4, count: 2, total: 3, parentId: null },
{ id: 5, count: 1, total: 1, parentId: 4 },
];
Here's my current code. I think here somehow I need go to the last level
and then starting from last item to top concataning prev level with current
level count
prop value that is why I made named IIFE.
let c = a.map(cat => {
const top = a.filter(v => cat.id === v.parentId);
let test;
return ({
...cat,
total: (test = categs => {
return categs.reduce((acc, val) => {
/* ??? */
return acc + val.count
}, cat.count);
})(top)
})
})
Upvotes: 3
Views: 1076
Reputation: 2032
If your collection is already sorted and need to calculate the total only. I came with the reduceRight
to save the numbers of loop and avoid mutation.
const base = [
{ id: 1, count: 2, parentId: null },
{ id: 2, count: 4, parentId: 1 },
{ id: 3, count: 0, parentId: 2 },
{ id: 6, count: 8, parentId: 3 },
{ id: 7, count: 8, parentId: 3 },
{ id: 8, count: 2, parentId: 7 },
{ id: 4, count: 2, parentId: null },
{ id: 5, count: 1, parentId: 4 },
];
const rs = base.reduceRight((acc, o) => {
const total = acc.filter(n => n.parentId === o.id).reduce((x, y) => x + y.total, 0)
return acc.concat({...o, total: total + o.count})
}, [])
console.log(rs.reverse());
Upvotes: 1
Reputation: 149020
Here's a stab at it:
const base = [
{ id: 1, count: 2, parentId: null },
{ id: 2, count: 4, parentId: 1 },
{ id: 3, count: 0, parentId: 2 },
{ id: 6, count: 8, parentId: 3 },
{ id: 7, count: 8, parentId: 3 },
{ id: 8, count: 2, parentId: 7 },
{ id: 4, count: 2, parentId: null },
{ id: 5, count: 1, parentId: 4 },
];
const getDescendants = ({ id }) =>
base.reduce((acc, n) => n.parentId === id ? [...acc, n, ...getDescendants(n)] : acc, []);
const expected =
base.map(record => ({
...record,
total: getDescendants(record).reduce((acc, cur) => acc + cur.count, record.count)
}));
console.log(expected);
The real trick here is the getDescendants
function. It gets an array of all elements whose parentId
prop is equal to the current record's id
concatenated with all the descendants of that node, as determined by recursively applying the function.
The solution isn't that efficient, but given that the question explicitly forbid the use of certain core programming constructs, I doubt that's a requirement.
Here's another approach, recursively modifying the original array:
const base = [
{ id: 1, count: 2, parentId: null },
{ id: 2, count: 4, parentId: 1 },
{ id: 3, count: 0, parentId: 2 },
{ id: 6, count: 8, parentId: 3 },
{ id: 7, count: 8, parentId: 3 },
{ id: 8, count: 2, parentId: 7 },
{ id: 4, count: 2, parentId: null },
{ id: 5, count: 1, parentId: 4 },
];
const addToTotal = (id, count) => id !== null && base.forEach(n => {
if (n.id === id) {
n.total = (n.total || 0) + count;
addToTotal(n.parentId, count);
}
});
base.forEach(n => {
n.total = (n.total || 0) + n.count;
addToTotal(n.parentId, n.count);
});
console.log(base);
Upvotes: 2