Yaroslav Pidmohylniy
Yaroslav Pidmohylniy

Reputation: 323

Sort flat array by parentId javascript

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

Answers (2)

tam.dangc
tam.dangc

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

p.s.w.g
p.s.w.g

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

Related Questions