3

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)
  })
})
1

2 Answers 2

2

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);

Sign up to request clarification or add additional context in comments.

Comments

1

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());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.