按照条件和自定义键对对象数组进行分组

8

我有如下对象数组:

[{

    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',
  }]
}

因此,首先应该按照创建日期对结果进行分组,然后每个组中的元素应进一步按定义的时间段进行分组。

我想,如果我能使用分组条件,就可以实现所需的结果。


4
请提供您尝试过的代码。有一些基于日期分组的问题,例如:“如何按日期对数组中的项进行分组?”(链接:https://dev59.com/4VYN5IYBdhLWcg3w2rSU) - adiga
3个回答

6

纯JS


使用 Array#reduceMap 实现分组。

  1. 按日期分组相当简单,你可以使用 Map 将数据按照 created_on.slice(0, 10) 进行分组。

  2. 然后我创建了一个名为 getSlot 的辅助函数,根据时间返回一个时间段。由于你的时间段相当宽泛,只测试小时部分即可。

  3. 接下来,对于每一组日期,再次使用 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));

输出截图


这里是图片描述


1

这是我尝试使用momentjslodash的内容。

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>


看起来它能工作,那么问题是什么? - Ravikumar
在@adiga的评论后,我尝试了一下,没有问题。 - sujeet
1
我明白了,我看到一个问题,如果记录在时间精确的槽边界创建,比如 2021-04-23 04:00:00,那么它应该放在哪个槽中呢?是 00:00-04:00 还是 04:00-08:00?我认为现有的代码不会将其放置在任何一个槽中。 - Ravikumar
感谢@Ravikumar指出,我会注意的。 - sujeet

0
我们可以通过简单的 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))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接