Home > OS >  Reduce timeseries array and aggregate data
Reduce timeseries array and aggregate data

Time:09-04

I see many posts/questions addressing this issue, so I assume it is not trivial. I am quite a beginner, looking for a more elegant solution.

I need to reduce this kind of array containing 1-minute detailed data into 5-minute data. Just computing the sum of consecutive values for 5 minutes, and then recreating a shorter array. Then the timestamp "created_at" should be the timepoint of the end of the 5-minute period.

let array = [
    { steps: 40, created_at: '2022-09-03T11:36:00.000Z' },
    { steps: 13, created_at: '2022-09-03T11:37:00.000Z' },
    { steps: 40, created_at: '2022-09-03T11:38:00.000Z' },
    { steps: 40, created_at: '2022-09-03T11:39:00.000Z' },
    { steps: 34, created_at: '2022-09-03T11:40:00.000Z' },
    { steps: 86, created_at: '2022-09-03T11:41:00.000Z' },
    { steps: 23, created_at: '2022-09-03T11:42:00.000Z' },
    { steps: 78, created_at: '2022-09-03T11:43:00.000Z' },
    { steps: 67, created_at: '2022-09-03T11:44:00.000Z' },
    { steps: 80, created_at: '2022-09-03T11:45:00.000Z' },
    { steps: 34, created_at: '2022-09-03T11:46:00.000Z' },
    { steps: 64, created_at: '2022-09-03T11:47:00.000Z' },
    { steps: 32, created_at: '2022-09-03T11:48:00.000Z' },
    { steps: 78, created_at: '2022-09-03T11:49:00.000Z' },
    { steps: 45, created_at: '2022-09-03T11:50:00.000Z' }
    ]

My solution is too complex I think:

const moment = require(`moment`);
const newArray = array.map(
    (item)=> {       
      const timestamp = moment(item.created_at).valueOf();
      const timeStampgroup =  Math.ceil((timestamp)/300000);
      return {...item, timeStampgroup: timeStampgroup}    
        }
    );
//console.log(newArray);  

const reducedArray = Array.from(newArray.reduce(
    (m, {timeStampgroup: timeStampgroup, steps}) => m.set(timeStampgroup, (m.get(timeStampgroup) || 0)   steps), new Map
  ), ([timeStampgroup, steps]) => ({timeStampgroup, steps}));
//console.log(reducedArray);

const result = reducedArray.map(entry => ({steps : entry.steps, created_at : moment(entry.timeStampgroup*300000).toISOString()}));
console.log(result);

[
 { steps: 167, created_at: '2022-09-03T11:40:00.000Z' },
 { steps: 334, created_at: '2022-09-03T11:45:00.000Z' },
 { steps: 253, created_at: '2022-09-03T11:50:00.000Z' }
]

Does anyone see a less complicated way to achieve the same result, in one pass maybe ?

Thanks a lot !

Lorenzo

CodePudding user response:

const array = [
        { steps: 40, created_at: '2022-09-03T11:36:00.000Z' },
        { steps: 13, created_at: '2022-09-03T11:37:00.000Z' },
        { steps: 40, created_at: '2022-09-03T11:38:00.000Z' },
        { steps: 40, created_at: '2022-09-03T11:39:00.000Z' },
        { steps: 34, created_at: '2022-09-03T11:40:00.000Z' },
        { steps: 86, created_at: '2022-09-03T11:41:00.000Z' },
        { steps: 23, created_at: '2022-09-03T11:42:00.000Z' },
        { steps: 78, created_at: '2022-09-03T11:43:00.000Z' },
        { steps: 67, created_at: '2022-09-03T11:44:00.000Z' },
        { steps: 80, created_at: '2022-09-03T11:45:00.000Z' },
        { steps: 34, created_at: '2022-09-03T11:46:00.000Z' },
        { steps: 64, created_at: '2022-09-03T11:47:00.000Z' },
        { steps: 32, created_at: '2022-09-03T11:48:00.000Z' },
        { steps: 78, created_at: '2022-09-03T11:49:00.000Z' },
        { steps: 45, created_at: '2022-09-03T11:50:00.000Z' }
        ]
        


const reducedArr = Array.from(
  array.reduce((m, item) => {
    const date = new Date(item.created_at)
    const key = Math.ceil(date.getMinutes()/5)*5;
    return m.set(key, (m.get(key) || 0)   item.steps)
  }, new Map()),
  ([created_at, steps]) => ({ created_at, steps })
)
console.log(reducedArr);

I edited This answer from @some-user on this question to remove the need for moment import.

CodePudding user response:

Like this?

const moment = require(`moment`)
require('moment-round')

const array = [
  { steps: 40, created_at: '2022-09-03T11:36:00.000Z' },
  { steps: 13, created_at: '2022-09-03T11:37:00.000Z' },
  { steps: 40, created_at: '2022-09-03T11:38:00.000Z' },
  { steps: 40, created_at: '2022-09-03T11:39:00.000Z' },
  { steps: 34, created_at: '2022-09-03T11:40:00.000Z' },
  { steps: 86, created_at: '2022-09-03T11:41:00.000Z' },
  { steps: 23, created_at: '2022-09-03T11:42:00.000Z' },
  { steps: 78, created_at: '2022-09-03T11:43:00.000Z' },
  { steps: 67, created_at: '2022-09-03T11:44:00.000Z' },
  { steps: 80, created_at: '2022-09-03T11:45:00.000Z' },
  { steps: 34, created_at: '2022-09-03T11:46:00.000Z' },
  { steps: 64, created_at: '2022-09-03T11:47:00.000Z' },
  { steps: 32, created_at: '2022-09-03T11:48:00.000Z' },
  { steps: 78, created_at: '2022-09-03T11:49:00.000Z' },
  { steps: 45, created_at: '2022-09-03T11:50:00.000Z' },
]

const result = Array.from(
  array.reduce((m, item) => {
    const key = moment(item.created_at).ceil(5, 'minutes').toISOString()
    return m.set(key, (m.get(key) || 0)   item.steps)
  }, new Map()),
  ([created_at, steps]) => ({ created_at, steps })
)

console.log(result)

CodePudding user response:

Assuming there are no gaps in the sequence, and that the array is sorted, you won't get a whole lot faster than this:

const results = [];

for (let i = 0; i < array.length; i  = 5) {
    const created_at = array[i].created_at;
    const steps = array.slice(i, i   5).reduce((acc, item) => acc   item.steps, 0);
    results.push({ created_at, steps });
}
  • Related