Reputation: 3867
I have a dataset that looks like this:
dataPoints = [
{ "x": 1, "y": 10, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 2, "y": 20, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 2, "y": 20, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 3, "y": 10, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 3, "y": 20, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
...
]
Desired output:
[
{ "x": 1, "y": 10, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 2, "y": 40, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 3, "y": 30, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
]
I wrote this to do basically what I want. What I'm trying to do is for all datapoints that have the same x, add up the values of y and make a single datapoint out of them. For the sake of this use case, we can either assume the "other thing" fields are going to be equal or that it doesn't matter if they differ (we can just take either datapoint's values for those):
let aggregatedDataPoints = new Map();
for (let dataPoint of dataPoints) {
if (aggregatedDataPoints.has(dataPoint.key.toString())) {
let prevPoint = aggregatedDataPoints.get(dataPoint.key.toString());
let newPoint = {...prevPoint};
newPoint.y = prevPoint.y + dataPoint.y;
aggregatedDataPoints.set(dataPoint.key.toString(), newPoint);
} else {
aggregatedDataPoints.set(dataPoint.key.toString(), dataPoint);
}
}
return aggregatedDataPoints;
However, I'm not really happy with this solution as it looks a bit messy. I'm trying to see if I can utilize a more functional or concise approach to do the same thing, but I'm having trouble getting anything more elegant to work.
Any help would be appreciated!
Upvotes: 1
Views: 34
Reputation: 1778
A slightly more idiomatic approach might look like this:
const result = {};
dataPoints.forEach(({x, y: currentY, ...other}) => {
const y = currentY + (result[x]?.y || 0)
result[x] = { ...other, x, y }
})
or if you don't like mutating properties:
Object.values(
dataPoints.reduce((result, {x, y: currentY, ...other}) => {
const y = currentY + (result[x]?.y || 0)
return {
...result,
[x]: { ...other, x, y }
}
}, {})
)
Upvotes: 1
Reputation: 64687
You could use .reduce
:
dataPoints = [
{ "x": 1234, "y": 14, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 1235 },
{ "x": 1234, "y": 34, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
].reduce((carry, current) => {
const existing = carry.find(el => el.x === current.x);
if (!existing) carry.push(current);
else if (existing.y && current.y) existing.y += current.y;
return carry;
}, [])
console.log(dataPoints);
Or, using a Map
:
const m = new Map();
[
{ "x": 1234, "y": 14, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 1235 },
{ "x": 1234, "y": 34, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
].forEach(el => {
const existing = m.get(el.x);
if (existing) existing.y += el.y;
else m.set(el.x, el);
});
console.log(Object.fromEntries(m.entries()));
Upvotes: 1
Reputation: 12949
you can just use a object to map those values (if only need the pair [x,y]
):
let dataPoints = [
{ "x": 1234, "y": 14, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
{ "x": 1235, },
{ "x": 1234, "y": 34, "someOtherUnrelatedThing": 15, "someOtherThing": 12},
];
let res = {};
dataPoints.forEach(el => res[el.x] = +el.y + (res[el.y] || 0) )
console.log(res);
Upvotes: 1