Reputation: 5348
I have an array of objects with 3 keys, lat, lng and value. e.g.
let arr = [
{ lat: 20, lng: 30, value: 3 },
{ lat: 25, lng: 25, value: 4 },
{ lat: 20, lng: 30, value: 6 },
{ lat: 30, lng: 40, value: -5 },
{ lat: 25, lng: 25, value: 7 },
];
I'd like to compute the sum for each lat/lng pair, expecting the following result:
let res = [
{ lat: 20, lng: 30, value: 9 },
{ lat: 25, lng: 25, value: 11 },
{ lat: 30, lng: 40, value: -5 }
];
I have tried to store the results into an intermediate object, like this:
const temp = res.reduce((acc, { lat, lng, value }) => {
acc[lat] = acc[lat] || {};
acc[lat][lng] = acc[lat][lng] || 0;
acc[lat][lng] += value;
return acc;
}, {});
so temp would look like this:
let temp = {
20: {
30: 9
},
25: {
25: 11
},
30: {
40: -5
}
};
And then convert it to the format I want:
let res = Object.keys(temp).reduce((acc, lat) => {
let tt = Object.keys(temp[lat]).map(lng => {
return {lat: lat, lng: lng, value: temp[lat][lng]};
});
acc.push(...tt);
return acc;
}, []);
This looks highly inefficient and I'm sure there is a smarter way to do it. I have looked at lodash's groupBy and sumBy but I couldn't put a solution together.
Upvotes: 1
Views: 879
Reputation: 1075705
I'd loop through and keep track of previous entries with a matching lat
and lng
in a Map
, then grab the Map
's values at the end:
const known = new Map();
for (const {lat, lng, value} of arr) {
const key = `${lat}:${lng}`;
const prev = known.get(key);
if (prev) {
prev.value += value;
} else {
known.set(key, {lat, lng, value});
}
}
const result = [...known.values()];
Live Example:
let arr = [
{ lat: 20, lng: 30, value: 3 },
{ lat: 25, lng: 25, value: 4 },
{ lat: 20, lng: 30, value: 6 },
{ lat: 30, lng: 40, value: -5 },
{ lat: 25, lng: 25, value: 7 },
];
const known = new Map();
for (const {lat, lng, value} of arr) {
const key = `${lat}:${lng}`;
const prev = known.get(key);
if (prev) {
prev.value += value;
} else {
known.set(key, {lat, lng, value});
}
}
const result = [...known.values()];
console.log(result);
You could do that with reduce
:
let arr = [
{ lat: 20, lng: 30, value: 3 },
{ lat: 25, lng: 25, value: 4 },
{ lat: 20, lng: 30, value: 6 },
{ lat: 30, lng: 40, value: -5 },
{ lat: 25, lng: 25, value: 7 },
];
const result = [...arr.reduce((map, {lat, lng, value}) => {
const key = `${lat}:${lng}`;
const prev = map.get(key);
if (prev) {
prev.value += value;
} else {
map.set(key, {lat, lng, value});
}
return map;
}, new Map()).values()];
console.log(result);
...but it doesn't buy you anything; it's just easier to get wrong. (I'm one of many who believe that unless you're doing functional programming with a predefined, reusable set of reducer functions, reduce
is just unnecessary complication.)
Upvotes: 2
Reputation: 13922
I would usually do something like this:
let arr = [
{ lat: 20, lng: 30, value: 3 },
{ lat: 25, lng: 25, value: 4 },
{ lat: 20, lng: 30, value: 6 },
{ lat: 30, lng: 40, value: -5 },
{ lat: 25, lng: 25, value: 7 },
];
const temp = arr.reduce((acc, { lat, lng, value }) => {
const key = `${lat}:${lng}`;
acc[key] = acc[key] || 0;
acc[key] += value;
return acc;
}, {});
console.log(temp);
Upvotes: -1
Reputation: 386868
You could take a flat object and get the values from it as result set.
An array of keys are used to get a joined key.
let array = [{ lat: 20, lng: 30, value: 3 }, { lat: 25, lng: 25, value: 4 }, { lat: 20, lng: 30, value: 6 }, { lat: 30, lng: 40, value: -5 }, { lat: 25, lng: 25, value: 7 }],
keys = ['lat', 'lng'],
result = Object.values(array.reduce((r, o) => {
const key = keys.map(k => o[k]).join('|');
if (!r[key]) r[key] = { ...o, value: 0 };
r[key].value += o.value;
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 3