8

I have below array of objects,

[{

    a: 1,
    created_on: '2021-04-23 10:00:01',
}, {
    b: 1,
    created_on: '2021-04-24 09:03:01',
}, {
    b: 1,
    created_on: '2021-04-24 13:03:01',
}]

First, I need to group by with date ignoring times.

So,

{
 "2021-04-23": [{
    a: 1,
    created_on: '2021-04-23 10:00:01',
  }],
....
}

If it were a date like created_on: '2021-04-23', I could use lodash/groupBy.

I still could use it but now it involves some condition to be applied to convert those created_on into date format.

Further, I also need to group by those each grouped by date result as such that their created_on falls within a time interval.

"SALES_SLOTS": [
    {
      "slot": "00:00-04:00"
    },
    {
      "slot": "04:00-08:00"
    }, 
    {
      "slot":"08:00-12:00"
    }, 
    {
      "slot":"12:00-16:00"
    },
    {
      "slot":"16:00-20:00"
    },
    {
      "slot":"20:00-24:00"
    }
  ],

So, if created_on: '2021-04-23 10:00:01', it should come in the group,

{
  "08:00-12:00": [{
     a: 1,
     created_on: '2021-04-23 10:00:01',
  }]
}

So, first results should be grouped by date from created on and then elements in each groups should be further group by defined time slots.

I suppose, if I could use group by condition, I could achieve the desired result.

1

3 Answers 3

6

Vanilla JS


Using Array#reduce with Map.

  1. Grouping by date is fairly simple, you can group the data by created_on.slice(0, 10) using a Map.

  2. Then I've created a helper function getSlot that returns a slot based on the time passed. Now, since your slots are fairly wide, testing just the hour part would be sufficient.

  3. Next, for every group of date, again group it by time using getSlot helper and Map and using Object.fromEntries you can get the desired object.

NOTE: I've used expressions instead of statements inside my code (using commas, defaulted parameters) which is just a preferential choice.

const 
  arr = [{ a: 1, created_on: "2021-04-23 10:00:01" }, { b: 1, created_on: "2021-04-24 09:03:01" }, { b: 1, created_on: "2021-04-24 13:03:01" }],
  
  slots = [{ slot: "00:00-04:00" }, { slot: "04:00-08:00" }, { slot: "08:00-12:00" }, { slot: "12:00-16:00" }, { slot: "16:00-20:00" }, { slot: "20:00-24:00" }],
      
  grpDate = Array.from(
    arr.reduce(
      (m, o, _i, _arr, d = o.created_on.slice(0, 10)) => (
        m.has(d) ? m.get(d).push(o) : m.set(d, [o]), m
      ),
      new Map()
    )
  ),
  
  getSlot = (time) =>
    slots
      .find(({ slot }) => time >= slot.slice(0, 2) && time < slot.slice(6, 8))
      .slot,
      
  grpSlot = grpDate.map(([k, v]) =>
    [k, Object.fromEntries(v.reduce(
      (m, o, _i, _arr, slot = getSlot(o.created_on.slice(11, 13))) => (
        m.has(slot) ? m.get(slot).push(o) : m.set(slot, [o]), m
      ),
      new Map()
    ))]
  );

console.log(Object.fromEntries(grpSlot));

Screenshot of the output


enter image description here

Sign up to request clarification or add additional context in comments.

Comments

1

This is what I tried using momentjs and lodash.

const bookings = [{
  a: 1,
  created_on: '2021-04-23 10:00:01',
}, {
  b: 1,
  created_on: '2021-04-24 09:03:01',
}, {
  b: 1,
  created_on: '2021-04-24 13:03:01',
}];


const groupedByDate = bookings.reduce((acc, e) => {
  const eDate = moment(e.created_on).format('YYYY-MM-DD');
  if (acc[eDate]) {
    acc[eDate].push(e)
  } else {
    acc[eDate] = [e];
  }
  return acc;
}, { })
const allSlots = ['00:00-04:00', '04:00-08:00', '08:00-12:00', '12:00-16:00','16:00-20:00','20:00-24:00'];
const grupedByDateAndSlots = _.mapValues(groupedByDate, (ele) => {
  return ele.reduce((acc, e) => {
    const createdOnDate = moment(e.created_on).format('YYYY-MM-DD');
    const foundSlot = allSlots.find((slot) => moment(e.created_on).isBetween(moment(`${createdOnDate} ${slot.split('-')[0]}`), moment(`${createdOnDate} ${slot.split('-')[1]}`)))
    if (acc[foundSlot]) {
      acc[foundSlot].push(e)
    } else {
      acc[foundSlot] = [e];
    }
    return acc;
  }, { })
})

console.log(grupedByDateAndSlots)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>

4 Comments

seems it works, then what is the problem?
I tried after @adiga commnet, no problem.
Got you, I see one problem what if a record is created on exact slots boundary time like 2021-04-23 04:00:00, in which slot it slot it should be placed? 00:00-04:00 or 04:00-08:00. I don't think it'll place any of these with existing code.
Thanks @Ravikumar for pointing out I will take care of that
0

We can achieve this with simple reduce like below.

const data =[{  a: 1,  created_on: '2021-04-23 04:00:00',}, {  b: 1,  created_on: '2021-04-24 09:03:01',}, {  b: 1,  created_on: '2021-04-24 13:03:01',}];

const allSlots = ['00:00-04:00', '04:00-08:00', '08:00-12:00', '12:00-16:00','16:00-20:00','20:00-24:00'].map(ele => ele.split('-'));




const result = data.reduce((acc, ele) => {
  // use can use format() to custom formats
  const [date, time] =ele.created_on.split(' ');
  const slot = allSlots.find(slot => slot[0] <= time && slot[1] >= time );
  if (!acc[date]) {
    acc[date] = {};
  }
  const key = `${slot[0]}-${slot[1]}`;
  if (!acc[date][key]) {
    acc[date][key] = [];
  }
  acc[date][key].push(ele);
  return acc;
}, {});

console.log(JSON.stringify(result))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.