skip to Main Content

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?

3

Answers


  1. 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; }
    Login or Signup to reply.
  2. 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 = 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 };
    });
    console.log(res)
    Login or Signup to reply.
  3. 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 map = arr.reduce((map, dates) => {
      dates.forEach(({date, displacement: num}) => {
        const found = map.get(date);
        found ? (found.sum += num, found.count++) : map.set(date, {sum: num, count: 1});
      });
      return map;
    }, new Map);
    
    const result = [...map].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 448 | ■ 1.00x x100k 173 | ■ 1.00x x10k 136 | ■ 1.00x  x1k 143
    Nina          2.99x x100k 134 |   2.14x x100k 370 |   1.96x x10k 266 |   2.05x  x1k 293
    0stone0       3.59x x100k 161 |   6.07x  x10k 105 |   7.28x  x1k  99 |   6.99x x100 100
    --------------------------------------------------------------------------------------- `
    

    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
    const map = arr.reduce((map, dates) => {
      dates.forEach(({date, displacement: num}) => {
        const found = map.get(date);
        found ? (found.sum += num, found.count++) : map.set(date, {sum: num, count: 1});
      });
      return map;
    }, new Map);
    
    [...map].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));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search