按具有条件和自定义键的对象数组分组
group by array of objects with condition and custom key
我有以下对象数组,
[{
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',
}]
首先,我需要使用忽略时间的日期进行分组。
所以,
{
"2021-04-23": [{
a: 1,
created_on: '2021-04-23 10:00:01',
}],
....
}
如果是 created_on: '2021-04-23'
这样的日期,我可以使用 lodash/groupBy
。
我仍然可以使用它,但现在它涉及一些条件,用于将那些 created_on
转换为日期格式。
此外,我还需要对每个按日期结果分组的对象进行分组,这样它们的 created_on
就在一个时间间隔内。
"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"
}
],
所以,如果created_on: '2021-04-23 10:00:01'
,应该进组,
{
"08:00-12:00": [{
a: 1,
created_on: '2021-04-23 10:00:01',
}]
}
因此,第一个结果应按创建日期分组,然后每个组中的元素应按定义的时间段进一步分组。
我想,如果我可以使用group by condition,我可以达到我想要的结果。
我们可以通过如下所示的简单 reduce
来实现。
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))
香草 JS
将 Array#reduce
与 Map
结合使用。
按日期分组非常简单,您可以使用 Map
.
按 created_on.slice(0, 10)
对数据进行分组
然后我创建了一个辅助函数 getSlot
,它 returns 一个基于时间流逝的槽。现在,由于您的插槽相当宽,因此仅测试小时部分就足够了。
接下来,对于每一组日期,再次使用 getSlot
助手和 Map
按时间分组,并使用 Object.fromEntries
可以获得所需的对象。
注意: 我在代码中使用了表达式而不是语句(使用逗号,默认参数)这只是一种优惠选择.
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));
输出的屏幕截图
这是我尝试使用 momentjs
和 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>
我有以下对象数组,
[{
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',
}]
首先,我需要使用忽略时间的日期进行分组。
所以,
{
"2021-04-23": [{
a: 1,
created_on: '2021-04-23 10:00:01',
}],
....
}
如果是 created_on: '2021-04-23'
这样的日期,我可以使用 lodash/groupBy
。
我仍然可以使用它,但现在它涉及一些条件,用于将那些 created_on
转换为日期格式。
此外,我还需要对每个按日期结果分组的对象进行分组,这样它们的 created_on
就在一个时间间隔内。
"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"
}
],
所以,如果created_on: '2021-04-23 10:00:01'
,应该进组,
{
"08:00-12:00": [{
a: 1,
created_on: '2021-04-23 10:00:01',
}]
}
因此,第一个结果应按创建日期分组,然后每个组中的元素应按定义的时间段进一步分组。
我想,如果我可以使用group by condition,我可以达到我想要的结果。
我们可以通过如下所示的简单 reduce
来实现。
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))
香草 JS
将 Array#reduce
与 Map
结合使用。
按日期分组非常简单,您可以使用
按Map
.created_on.slice(0, 10)
对数据进行分组然后我创建了一个辅助函数
getSlot
,它 returns 一个基于时间流逝的槽。现在,由于您的插槽相当宽,因此仅测试小时部分就足够了。接下来,对于每一组日期,再次使用
getSlot
助手和Map
按时间分组,并使用Object.fromEntries
可以获得所需的对象。
注意: 我在代码中使用了表达式而不是语句(使用逗号,默认参数)这只是一种优惠选择.
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));
输出的屏幕截图
这是我尝试使用 momentjs
和 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>