Home > Mobile >  The sum of keys in an array of objects for each day
The sum of keys in an array of objects for each day

Time:01-03

I am having a problem: I am trying to calculate the sum of each key for each day. However, I cannot get the desired result. Perhaps you can help fix the error?

This is my way of solving the problem:

const result = input.reduce((acc, curr) => {
  const node = acc.find((i) => i.date === curr.date);

  if (node) {
    return [...acc.filter((i) => i.date !== curr.date), { ...node, date: curr.date, [curr.source]: [curr.source].length   1 }];
  }

  return [...acc, { date: curr.date, [curr.source]: [curr.source].length }];
}, []);
const input = [
  {
    date: "01-01-2021",
    source: "TT",
  },
  {
    date: "01-01-2021",
    source: "TT",
  },
  {
    date: "02-01-2021",
    source: "FB",
  },
  {
    date: "02-01-2021",
    source: "TT",
  },
];

**Expected result: **

const output = [
  {
    date: "01-01-2021",
    TT: 2,
  },
  {
    date: "02-01-2021",
    TT: 1,
    FB: 1,
  },
];

And this is what I get now:

const result = [
  {
    date: "01-01-2021",
    TT: 2,
  },
  {
    date: "02-01-2021",
    TT: 2,
    FB: 1,
  },
];

CodePudding user response:

The issue with your code is the following part in your if-statement:

[curr.source].length   1

Here you're creating an array with curr.source (1 element), grabbing its length which will always be 1, and then adding 1 to it, giving you 2. Instead, you can change this line to be:

(node[curr.source] ?? 0)   1

This will grab the source's value from your found node, and it will add one to the current value. If the value hasn't been set on the node, it will default to 0 by using ?? 0 (here ?? is the Nullish coalescing operator - it could be replaced with || if you need better browser support). I would also suggest defaulting your node value to an empty object if it isn't found when using .find() by using ?? {}, which will allow you to remove your inner if-statement and run that portion of code each time:

const input = [ { date: "01-01-2021", source: "TT", }, { date: "01-01-2021", source: "TT", }, { date: "02-01-2021", source: "FB", }, { date: "02-01-2021", source: "TT", }, ];

const result = input.reduce((acc, curr) => {
  const node = acc.find((i) => i.date === curr.date) ?? {};
  return [...acc.filter((i) => i.date !== curr.date), { ...node, date: curr.date, [curr.source]: (node[curr.source] ?? 0)   1 }];
}, []);

console.log(result);

Another option that would be more efficient is to build a Map/object that is keyed by your date key from your objects. For each iteration of your reduce method you can get the current accumulated object at your current date from the Map, and update the source count based on the current source. You can then transform the Map into an array of your accumulated objects by using Array.from() on that Map's .values() iterator:

const input = [ { date: "01-01-2021", source: "TT", }, { date: "01-01-2021", source: "TT", }, { date: "02-01-2021", source: "FB", }, { date: "02-01-2021", source: "TT", }, ];

const res = Array.from(input.reduce((acc, {date, source}) => {
  const seen = acc.get(date) ?? {};
  return acc.set(date, {...seen, date, [source]: (seen[source] ?? 0)   1});
}, new Map).values());
console.log(res);

CodePudding user response:

You could group with an object and increment the given source property.

const input = [
  { date: "01-01-2021", source: "TT" },
  { date: "01-01-2021", source: "TT" },
  { date: "02-01-2021", source: "FB" },
  { date: "02-01-2021", source: "TT" }
];

const result = Object.values(input.reduce((r, { date, source }) => {
  r[date] ??= { date };
  r[date][source] = (r[date][source] || 0)   1;
  return r;
}, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

  • Related