I have an array like this where an object is inside nested arrays:
let arr = [
[
{date: 1578787200000, displacement: 0},
{date: 1580860800000, displacement: 1},
{date: 1593302400000, displacement: 2},
{date: 1606780800000, displacement: 3}
],
[
{date: 1578787200000, displacement: 10},
{date: 1580860800000, displacement: 20},
{date: 1593302400000, displacement: 30},
{date: 1606780800000, displacement: 40}
]
]
and want to group the objects by ‘date’ whilst also averaging the ‘displacement’ values so the result looks like this:
[
{date: 1578787200000, displacement: 5},
{date: 1580860800000, displacement: 10.5},
{date: 1593302400000, displacement: 16.5},
{date: 1606780800000, displacement: 21.5}
]
I have tried .reduce() combined with .forEach() to sum the displacements per date but can’t get it to output exactly what I’m after.
e.g.
let res = arr.reduce(function (prev, currentArr) {
currentArr.forEach(function (obj) {
(prev.date == obj.date || []).push(prev.displacement + obj.displacement)
}, 0);
return prev;
});
What is the best way to accomplish this?
nworbi is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
2
You could group first and then get average per group and result.
const
data = [[{ date: 1578787200000, displacement: 0 }, { date: 1580860800000, displacement: 1 }, { date: 1593302400000, displacement: 2 }, { date: 1606780800000, displacement: 3 }], [ { date: 1578787200000, displacement: 10 }, { date: 1580860800000, displacement: 20 }, { date: 1593302400000, displacement: 30 }, { date: 1606780800000, displacement: 40 }]],
getAverage = (array, key) => array.reduce((t, o) => t + o[key], 0) / array.length,
result = Object
.values(Object.groupBy(data.flat(), ({ date }) => date))
.map(group => ({
date: group[0].date,
displacement: getAverage(group, 'displacement')
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can group the items with a map:
let arr = [
[
{date: 1578787200000, displacement: 0},
{date: 1580860800000, displacement: 1},
{date: 1593302400000, displacement: 2},
{date: 1606780800000, displacement: 3}
],
[
{date: 1578787200000, displacement: 10},
{date: 1580860800000, displacement: 20},
{date: 1593302400000, displacement: 30},
{date: 1606780800000, displacement: 40}
]
]
const result = arr.reduce((r, dates) => {
dates.forEach(({date, displacement: num}) => {
const found = r.map.get(date);
if(found) found.sum += num, found.count++;
else r.map.set(date, r.arr[r.arr.length] = {date, sum: num, count: 1});
});
return r;
}, {map: new Map, arr: []}).arr.map(({date, sum, count}) => ({date, displacement: sum / count}));
console.log(result);
And a benchmark:
` Chrome/128
---------------------------------------------------------------------------------------
> n=2 | n=20 | n=200 | n=2000
Alexander ■ 1.00x x1m 324 | ■ 1.00x x100k 162 | ■ 1.00x x10k 148 | ■ 1.00x x1k 151
Nina 3.98x x100k 129 | 2.23x x100k 362 | 1.75x x10k 259 | 1.99x x1k 301
0stone0 4.94x x100k 160 | 6.48x x10k 105 | 6.69x x1k 99 | 6.95x x100 105
--------------------------------------------------------------------------------------- `
Open in the playground
let $chunk = () => [
[
{date: 1578787200000, displacement: 0},
{date: 1580860800000, displacement: 1},
{date: 1593302400000, displacement: 2},
{date: 1606780800000, displacement: 3}
],
[
{date: 1578787200000, displacement: 10},
{date: 1580860800000, displacement: 20},
{date: 1593302400000, displacement: 30},
{date: 1606780800000, displacement: 40}
]
]
const $input = [], arr = $input;
// @benchmark Alexander
arr.reduce((r, dates) => {
dates.forEach(({date, displacement: num}) => {
const found = r.map.get(date);
if(found) found.sum += num, found.count++;
else r.map.set(date, r.arr[r.arr.length] = {date, sum: num, count: 1});
});
return r;
}, {map: new Map, arr: []}).arr.map(({date, sum, count}) => ({date, displacement: sum / count}));
// @benchmark 0stone0
let res = arr.flat().reduce((p, c) => {
if (!p[c.date]) p[c.date] = { date: c.date, count: 0, displacement: 0 };
p[c.date].displacement += c.displacement;
p[c.date].count ++;
return p;
}, {});
res = Object.values(res).map(({date, displacement, count}) => {
return { date, displacement: displacement / count };
});
// @benchmark Nina
const data = arr;
const getAverage = (array, key) => array.reduce((t, o) => t + o[key], 0) / array.length;
Object
.values(Object.groupBy(data.flat(), ({ date }) => date))
.map(group => ({
date: group[0].date,
displacement: getAverage(group, 'displacement')
}));
/*@skip*/ fetch('https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js').then(r => r.text().then(eval));
You’ll need to know the amount of same dates before you can calculate the avarege.
Use reduce to add all displacement on the same date, keep track of the count.
Then map over the grouped result and caculate the avarage
let arr = [[{date: 1578787200000, displacement: 0}, {date: 1580860800000, displacement: 1}, {date: 1593302400000, displacement: 2}, {date: 1606780800000, displacement: 3} ], [{date: 1578787200000, displacement: 10}, {date: 1580860800000, displacement: 20}, {date: 1593302400000, displacement: 30}, {date: 1606780800000, displacement: 40} ] ]
let res = Object.values(
arr.flat().reduce((p, c) => {
if (!p[c.date]) p[c.date] = { date: c.date, count: 0, displacement: 0 };
p[c.date].displacement += c.displacement;
p[c.date].count ++;
return p;
}, {}))
.map(({date, displacement, count}) => ({ date, displacement: displacement / count }));
console.log(res)