Reputation: 359
I'm looking to reduce an array of objects by matching identical date strings and add the totals together of those matching dates and combine them into a single object. I could have an array several thousand items long so I'm trying to increase (where ever possible) and complexity.
// before
let objArr = [
{
date: '01/01/2018',
total: 1
},
{
date: '01/01/2018',
total: 2
},
{
date: '01/02/2018',
total: 3
},
{
date: '01/02/2018',
total: 4
},
...
]
// final result
let finalArr = [
{
date: '01/01/2018',
total: 3
},
{
date: '01/02/2018',
total: 7
},
...
]
I couldn't seem to get the hang of reducing them using reduce:
objArr.reduce((acc, obj) => {
acc.set(obj.date, (acc.get([obj.date]) || 0) + obj.total);
return acc;
}, new Map())
The result always ends up with the wrong totals or the last several array objects look like:
// bad output
badArray = [
...,
{
date: '01/02/2018',
total: 4
},
{
date: undefined,
total: NaN
},
{
date: undefined,
total: NaN
}
]
I wrote a script to check to ensure all the values in the date
and total
properties existed the way they needed to but I still end up with a bad array. The assumption here is my reduce function isn't right.
Upvotes: 2
Views: 367
Reputation: 1298
I think Mark Meyer's answer is good, but this is also another option that uses an empty array as the initial value instead of a Map (maintaining the original structure of your input throughout):
let objArr = [{date: '01/01/2018',total: 1},{date: '01/01/2018',total: 2},{date: '01/02/2018',total: 3},{date: '01/02/2018',total: 4},];
const result = objArr.reduce((acc, obj) => {
const existingObj = acc.find((o) => o.date === obj.date);
if (existingObj) {
existingObj.total += obj.total;
} else {
acc.push(obj);
}
return acc;
}, []);
console.log(result);
Note that the benefit of using an empty array as your initial value is that it maintains the original structure of your input (so you wouldn't have to iterate through the generated Map afterwards and convert it into an array of objects). :)
Upvotes: 0
Reputation: 92460
Your code is almost right. The problem is that you are setting the Map key to the date but trying to get
the item by passing an array with the date rather than the date alone.
You are using: acc.get([obj.date])
, when you probably want: acc.get(obj.date)
(without the [ ]
)
let objArr = [{date: '01/01/2018',total: 1},{date: '01/01/2018',total: 2},{date: '01/02/2018',total: 3},{date: '01/02/2018',total: 4},]
let s = objArr.reduce((acc, obj) => {
// not acc.get([obj.date]) !
acc.set(obj.date, (acc.get(obj.date) || 0) + obj.total);
return acc;
}, new Map())
// turn the Map into something that will display:
console.log([...s.entries()].map(([date, total]) => ({date, total})))
Upvotes: 3