Reputation: 13506
This question is come from how-to-sum-an-array-for-each-id-and-create-new-array-in-react,OP needs to sum data by id
const data = [{"id": "One", "number": 100},
{"id": "One", "number": 150},
{"id": "One", "number": 200},
{"id": "Two", "number": 50},
{"id": "Two", "number": 100},
{"id": "Three", "number": 10},
{"id": "Three", "number": 90}]
In order to do it,I just use a traditional way(check with if
) to do it with reduce()
let result2 = data.reduce((a, v) => {
let obj = a.find(i => i.id == v.id);
if (obj) {
obj.number += v.number;
} else {
a.push(v);
}
return a;
}, [])
console.log(result2);
And I found another answer is more elegant(one-liner):
let result3 = data.reduce((acc, {id, number}) => ({...acc, [id]: {id, number: acc[id] ? acc[id].number + number: number}}), {});
console.log(Object.values(result3));
The question is that when I ran the two methods seperately,I can got the expected result(jsfiddle1 and jsfiddle2)
However,if I ran them together,the seconds result(from result3
) is not as expected(jsfiddle 3)
I do not know why this happen,can anyone help me to analysis this?
Also,I want to know if there are a more elegant one-liner solution to do it.
Thanks in advance!
Upvotes: 0
Views: 50
Reputation: 26696
Your version mutates, as has been pointed out. It mutates the objects that are in the data
array. So if you use the data
array after your let result2 =
then the data
array has been mutated.
If your fiddle looked like:
let result3 = data.reduce(...);
console.log(result3);
let result2 = data.reduce(...);
console.log(result2);
then the answers will be the same values, because the data array doesn't get mutated until your result2
code runs. But any other users of the data
array, in the future, would have to deal with the mutated values.
Meanwhile, you can either do what has been recommended in the comments and shallow copy:
let result2 = data.reduce((a, v) => {
let obj = a.find(i => i.id == v.id);
if (obj) {
obj.number += v.number;
} else {
const copy = { ...v }; // <-- don't mutate the original
a.push(copy);
}
return a;
}, [])
Another simple way of doing this (exact data set) is to rebuild the data after the fact.
const dictionary = data.reduce((dict, { id, number }) => {
const currentSum = dict[id] ?? 0;
dict[id] = currentSum + number;
return dict;
}, {});
const results = Object.entries(dictionary)
.map(([id, number]) => ({ id, number }));
Upvotes: 3
Reputation: 39
Another way to archive it is to utilize the Map
data structure in js
const result = Array.from(
data.reduce(
(acc, next) => acc.set(next.id, next.number + acc.get(next.id) || 0),
new Map(),
),
([id, number]) => ({ id, number }),
);
console.log(result)
Upvotes: 0