Skip to content
Advertisement

Reduce array of arrays, concat data by dates to week data and sum the values

So I have an peculiar set of data:

data: [
  ['2022-01-17T16:29:24', 30], // these below would aggregate to 210
  ['2022-01-17T17:37:24', 30],
  ['2022-01-17T17:41:40', 30],
  ['2022-01-17T17:41:48', 30],
  ['2022-01-21T12:50:18', 30],
  ['2022-01-21T12:50:18', 30],
  ['2022-01-21T12:50:29', 30],        
  ['2022-01-12T12:50:18', 30], // these below would aggregate to 90
  ['2022-01-12T12:50:18', 30],
  ['2022-01-12T12:50:29', 30]
]

I would need to reduce this to sets of week data. I would convert dates to start of week to get the week data moment(day).startOf('isoWeek') and then concat the data on similar dates and reduce it to single entry.

Ending result would look like:

data: [
  ['2022-01-17T00:00:00', 210],
  ['2022-01-10T00:00:00', 90]
]

But I am looking for suggestions how to use .reduce() or any other more modern solutions for this that would be performant on large sets. Right now I have nested for loops for this which (aside from looking bad) probably perform quite poorly because it iterates multiple times over same array. One requirement is that moment should be used for date manipulation, please try to avoid Date obj’s.

Right now I have something like this:

mergeDataToWeek(data: any[]) {
  let reducedArrays = [];
  for (let arr of data) {
    if (moment(arr[0]).isValid() && arr[1] > 0) {
      arr[0] = moment(arr[0]).startOf('isoWeek').toISOString(true);
      if (!reducedArrays.some(reduced => reduced[0] == (arr[0]))) {
        reducedArrays.push(arr);
      } else {
        let target = reducedArrays.find(reduced => reduced[0] == arr[0]);
        target[1] += arr[1];
      }
    }
  }
  console.log(reducedArrays);
  return reducedArrays;
}

Advertisement

Answer

I’m trying to solve it using reduce, but the result will be an object. Like this:

const moment = require("moment")
var x = [
    ['2022-01-17T16:29:24', 30], 
    ['2022-01-17T17:37:24', 30],
    ['2022-01-17T17:41:40', 30],
    ['2022-01-17T17:41:48', 30],
    ['2022-01-21T12:50:18', 30],
    ['2022-01-21T12:50:18', 30],
    ['2022-01-21T12:50:29', 30],        
    ['2022-01-12T12:50:18', 30], 
    ['2022-01-12T12:50:18', 30],
    ['2022-01-12T12:50:29', 30]
  ]

// solution
var y = x.reduce((a,b) => {
    const key = moment(b[0]).startOf("isoWeek").format()
    a[key] = a[key]? a[key]+b[1]: b[1]
    return a
}, {})

console.log(y)

Output:

{ 
  "2022-01-17T00:00:00+07:00": 210,
  "2022-01-10T00:00:00+07:00": 90 
}

But if you want it as array as yours, we can convert it by:

var z = Object.keys(y).map(i => [i,y[i]])

// OR THIS -- Thanks to @ghybs
var z = Object.entries(y)

console.log(z)

Output:

[
  [ '2022-01-17T00:00:00+07:00', 210 ],
  [ '2022-01-10T00:00:00+07:00', 90 ]
]

Although it’s not the best solution, but I think it will much more readable than nested loop.

Advertisement