Reputation: 3810
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.
Upvotes: 8
Views: 3330
Reputation: 8168
Array#reduce
with Map
.Grouping by date is fairly simple, you can group the data by created_on.slice(0, 10)
using a Map
.
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.
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));
Upvotes: 6
Reputation: 2205
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))
Upvotes: 0
Reputation: 3810
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>
Upvotes: 1