Dibish
Dibish

Reputation: 9293

Group an array of time object

I have a requirement to group an array of objects based on time interval. The input looks like:

[
  {
    _id: {
      hour: 0,
      interval: '0'
    },
    time: '0:0',
    count: 10
  },
  {
    _id: {
      hour: 0,
      interval: '15'
    },
    time: '0:15',
    count: 5
  },
  {
    _id: {
      hour: 0,
      interval: '30'
    },
    time: '0:30',
    count: 1
  },
  {
    _id: {
      hour: 0,
      interval: '45'
    },
    time: '0:45',
    count: 2
  },
  {
    _id: {
      hour: 1,
      interval: '0'
    },
    time: '1:0',
    count: 4
  },
  {
    _id: {
      hour: 1,
      interval: '15'
    },
    time: '1:15',
    count: 3
  },
  {
    _id: {
      hour: 1,
      interval: '30'
    },
    time: '1:30',
    count: 5
  },
  {
    _id: {
      hour: 1,
      interval: '45'
    },
    time: '1:45',
    count: 1
  }
]

My desired output:

[
      {
        "time": "0",
        "0": 10,
        "15": 5
        "30": 1,
        "45": 2
      },
      {
        "time": "1",
        "0": 4,
        "15": 3
        "30": 5,
        "45": 1
      }
    ]

I tried to use the following code to group the objects, which works to an extent, but I'm stuck on what to do next:

const a = [ { _id: { hour: 0, interval: '0' }, time: '0:0', count: 10 }, { _id: { hour: 0, interval: '15' }, time: '0:15', count: 5 }, { _id: { hour: 0, interval: '30' }, time: '0:30', count: 1 }, { _id: { hour: 0, interval: '45' }, time: '0:45', count: 2 }, { _id: { hour: 1, interval: '0' }, time: '1:0', count: 4 }, { _id: { hour: 1, interval: '15' }, time: '1:15', count: 3 }, { _id: { hour: 1, interval: '30' }, time: '1:30', count: 5 }, { _id: { hour: 1, interval: '45' }, time: '1:45', count: 1 }]

var group = a.reduce((r, a) => {
  console.log("a", a);
  console.log('r', r);
  r[a._id.hour] = [...r[a._id.hour] || [], a];
  return r;
}, {});
console.log("group", group);

Upvotes: 1

Views: 77

Answers (3)

StepUp
StepUp

Reputation: 38094

You can group object by reduce method. So at first you need to group by hour and then just add interval properties from each iteration of reduce method to the hour property:

const result = arr.reduce((a, c) => {
   a[c._id.hour] = a[c._id.hour] || {};
   a[c._id.hour].time = c._id.hour;
   a[c._id.hour][c._id.interval] = c.count;    
   return a;
 }, {})

 console.log(result);

An example:

let arr = [
  {
    _id: {
      hour: 0,
      interval: '0'
    },
    time: '0:0',
    count: 10
  },
  {
    _id: {
      hour: 0,
      interval: '15'
    },
    time: '0:15',
    count: 5
  },
  {
    _id: {
      hour: 0,
      interval: '30'
    },
    time: '0:30',
    count: 1
  },
  {
    _id: {
      hour: 0,
      interval: '45'
    },
    time: '0:45',
    count: 2
  },
  {
    _id: {
      hour: 1,
      interval: '0'
    },
    time: '1:0',
    count: 4
  },
  {
    _id: {
      hour: 1,
      interval: '15'
    },
    time: '1:15',
    count: 3
  },
  {
    _id: {
      hour: 1,
      interval: '30'
    },
    time: '1:30',
    count: 5
  },
  {
    _id: {
      hour: 1,
      interval: '45'
    },
    time: '1:45',
    count: 1
  }
]


 const result = arr.reduce((a, c) => {
   a[c._id.hour] = a[c._id.hour] || {};
   a[c._id.hour].time = c._id.hour;
   a[c._id.hour][c._id.interval] = c.count;

   return a;
 }, {})

 console.log(result);

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 191976

Reduce the array, and create an object for each _id.time. Assign the current [interval] = count to the object. Get the entries, and use Array.from() to convert the entries to an array of the required form:

const arr = [{"_id":{"hour":0,"interval":"0"},"time":"0:0","count":10},{"_id":{"hour":0,"interval":"15"},"time":"0:15","count":5},{"_id":{"hour":0,"interval":"30"},"time":"0:30","count":1},{"_id":{"hour":0,"interval":"45"},"time":"0:45","count":2},{"_id":{"hour":1,"interval":"0"},"time":"1:0","count":4},{"_id":{"hour":1,"interval":"15"},"time":"1:15","count":3},{"_id":{"hour":1,"interval":"30"},"time":"1:30","count":5},{"_id":{"hour":1,"interval":"45"},"time":"1:45","count":1}];

// convert the entries to an array
const result = Array.from(Object.entries(
    arr.reduce((r, o) => {
      const { hour, interval } = o._id; // get the hour and interval
      
      if(!r[hour]) r[hour] =  {}; // create a the hour object
     
      r[hour][interval] = o.count; // add the interval and count
      
      return r;
    }, {})
  ), ([time, values]) => ({ time, ...values })); // generate the result objects

console.log(result)

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370699

Check if the object with that hour exists in the accumulator object first - if it doesn't, create one, then assign count to that object's [interval] property, and get the Object.values at the end to turn it back into an array:

const input=[{_id:{hour:0,interval:"0"},time:"0:0",count:10},{_id:{hour:0,interval:"15"},time:"0:15",count:5},{_id:{hour:0,interval:"30"},time:"0:30",count:1},{_id:{hour:0,interval:"45"},time:"0:45",count:2},{_id:{hour:1,interval:"0"},time:"1:0",count:4},{_id:{hour:1,interval:"15"},time:"1:15",count:3},{_id:{hour:1,interval:"30"},time:"1:30",count:5},{_id:{hour:1,interval:"45"},time:"1:45",count:1}];

const groupedObj = {};
for (const { _id: { hour, interval }, count } of input) {
  if (!groupedObj[hour]) {
    groupedObj[hour] = { time: hour };
  }
  groupedObj[hour][interval] = count;
}
const output = Object.values(groupedObj);
console.log(output);

Upvotes: 2

Related Questions