Sum all object items in array with dynamic property in JavaScript

Viewed 432

I have the following array:

[
     {category: 'Category 1', 'Apr 2021' : 10, 'Mar 2021' : 20, 'Feb 2021': 5},
     {category: 'Category 2', 'Apr 2021' : 8, 'Mar 2021' : 2, 'Feb 2021': 15},
     {category: 'Category 3', 'Apr 2021' : 7, 'Mar 2021' : 1, 'Feb 2021': 5}
]

I would like to add a total row with the sum of categories in the array.

Example:

[
     {category: 'Category 1', 'Apr 2021' : 10, 'Mar 2021' : 20, 'Feb 2021': 5},
     {category: 'Category 2', 'Apr 2021' : 8, 'Mar 2021' : 2, 'Feb 2021': 15},
     {category: 'Category 3', 'Apr 2021' : 7, 'Mar 2021' : 1, 'Feb 2021': 5},
     {category: 'Total',      'Apr 2021' : 25, 'Mar 2021' : 23, 'Feb 2021': 30}
]

I tried looping through all columns using Object.keys. However, it feels like there may be an easier way to achieve this.

What is the best way to calculate the total row?

Please note that "Apr 2021", "Mar 2021" are not fixed so I won't be able to hardcode them.

3 Answers

Please note that "Apr 2021", "Mar 2021" are not fixed so I won't be able to hardcode them.

In your case the properties are dynamic. You can try this way with 2 steps:

  1. Using Array#reduce to loop all objects of the array
  2. At each object, you sum the value of each properties by iteration the object due to dynamic property like this
for(const [key, value] of Object.entries(curr)

const array = [
     {category: 'Category 1', 'Apr 2021' : 10, 'Mar 2021' : 20, 'Feb 2021': 5},
     {category: 'Category 2', 'Apr 2021' : 8, 'Mar 2021' : 2, 'Feb 2021': 15},
     {category: 'Category 3', 'Apr 2021' : 7, 'Mar 2021' : 1, 'Feb 2021': 5}
];
const total = array.reduce((acc, curr) => {
  for(const [key, value] of Object.entries(curr)){
    if (key !== 'category') {
      acc[key] ??= 0;
      acc[key] += value;
    }
  }
  
  return acc;
}, {});
console.log([...array, {category: "Total", ...total}]);
.as-console-wrapper { max-height: 100% !important; top: 0; }

const data = [
     {category: 'Category 1', 'Apr 2021' : 10, 'Mar 2021' : 20, 'Feb 2021': 5},
     {category: 'Category 2', 'Apr 2021' : 8, 'Mar 2021' : 2, 'Feb 2021': 15},
     {category: 'Category 3', 'Apr 2021' : 7, 'Mar 2021' : 1, 'Feb 2021': 5}
];
const res = data.reduce((result, item) => {
  const keys = Object.keys(item);
  keys.forEach(key => {
    if (key === 'category') { return; }
    result[key] = result[key] ? result[key] + item[key] : item[key];
  });
  return result;
}, { category: 'Total' });
data.push(res);
console.log(data);

Clean declarative way:

const input = [
     {category: 'Category 1', 'Apr 2021' : 10, 'Mar 2021' : 20, 'Feb 2021': 5},
     {category: 'Category 2', 'Apr 2021' : 8, 'Mar 2021' : 2, 'Feb 2021': 15},
     {category: 'Category 3', 'Apr 2021' : 7, 'Mar 2021' : 1, 'Feb 2021': 5}
]

const totalRow = input.reduce((acc, e) => {
    Object.keys(e)
      .filter(k => k !== 'category')
      .forEach(k => acc[k] ? acc[k] += e[k] : acc[k] = e[k])
    return acc
}, {category: 'Total'})

const res = [...input, totalRow]
console.log(res)

Related