Summing values in nested objects where key value pairs match

Viewed 45

I have a problem trying to sum values when they are within an object.

Given the following array:

const data = [
  {
    id: "tom",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "tom",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "dick",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "harry",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "harry",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
];

I need to sum each of the numbers in the values object where the id matches, giving the following result:

const result = [
  {
    id: "tom",
    values: { ten: 20, twenty: 40, thirty: 60 },
  },
  {
    id: "dick",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "harry",
    values: { ten: 20, twenty: 40, thirty: 60 },
  }
];

Is there an easy way to achieve this?

Maybe some combination of reduce and map?

2 Answers

Using Array#reduce to accumulate the desired data. For this iterating over the object and looks if in the new accumulated (at start empty) result-object exists a property with this id. If not, create one and add the object there. Otherwise add to the object the value of each property of your values-object.

At last use Object#values to get the desired array out of the object.

const data = [
  {
    id: "tom",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "tom",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "dick",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "harry",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "harry",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
];

let res = Object.values(data.reduce((acc, cur) => {
    if (!acc.hasOwnProperty(cur.id)) {
        acc[cur.id] = { id: cur.id, values: cur.values};
    } else {
        Object.entries(cur.values).forEach(([key,value]) => acc[cur.id].values[key] += value);
    }
    return acc;
}, {}));

console.log(res);

Or old-fashion but simple loops style

const data = [
  {
    id: "tom",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "tom",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "dick",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "harry",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
  {
    id: "harry",
    values: { ten: 10, twenty: 20, thirty: 30 },
  },
];

// New data
const newData = [];

// Loop
for(let i = 0; i < data.length; i++) {
  const item = data[i];
  let push = true;
  for(let i = 0; i < newData.length; i++) if(item.id === newData[i].id) {
    push = false;
    for(const key in newData[i].values) newData[i].values[key] += item.values[key];
  }
  if(push) newData.push(item);
}

// Log
console.log(newData)

Related