JS 日期数组如何按天分组

问题描述 投票:0回答:5

我正在尝试找出最优化且循环次数最少的方法来对我的 js 日期对象数组进行分组:(请注意,这是浏览器控制台输出,它实际上是真正的 JS 日期,例如 new Date())

[Sat Aug 08 2015 08:30:00 GMT+0200 (Central Europe Daylight Time), Sat Aug 08 2015 09:30:00 GMT+0200 (Central Europe Daylight Time), Sun Aug 09 2015 08:30:00 GMT+0200 (Central Europe Daylight Time), Sun Aug 09 2015 09:30:00 GMT+0200 (Central Europe Daylight Time), Mon Aug 10 2015 18:00:00 GMT+0200 (Central Europe Daylight Time), Mon Aug 10 2015 23:00:00 GMT+0200 (Central Europe Daylight Time), Tue Aug 11 2015 18:00:00 GMT+0200 (Central Europe Daylight Time), Tue Aug 11 2015 23:00:00 GMT+0200 (Central Europe Daylight Time), Wed Aug 12 2015 18:00:00 GMT+0200 (Central Europe Daylight Time), Wed Aug 12 2015 23:00:00 GMT+0200 (Central Europe Daylight Time)]

将同一天的每个日期放在“块”内组织数组,这样我就可以将其显示在 UI“8 月 8 日”上,并显示当天内的 2 个或多少个日期。

例如:

[{day: 'Aug 08', times:[Sat Aug 08 2015 08:30:00 GMT+0200 (Central Europe Daylight Time), Sat Aug 08 2015 09:30:00 GMT+0200 (Central Europe Daylight Time)]}]

我目前的想法是

var startDays = _.map(occurences, function (date) {
  return moment(date).startOf('day').format();
});

之后获得独特的日子:

_.uniq(startDays, true)

在我得到独特的日子之后,另一个循环将同一天添加到这个组中,正如你现在所看到的,你可能会明白为什么我不喜欢它,这就是为什么我很想得到一些聪明的帮助,因为没有什么可以得到的我的脑子里有这个。谢谢。

javascript underscore.js momentjs
5个回答
71
投票

Underscore 具有 _.groupBy 函数,它应该完全满足您的需求:

var groups = _.groupBy(occurences, function (date) {
  return moment(date).startOf('day').format();
});

这将返回一个对象,其中每个键都是一天,值是一个包含该天所有事件的数组。

要将对象转换为与问题中形式相同的数组,您可以使用映射:

var result = _.map(groups, function(group, day){
    return {
        day: day,
        times: group
    }
});

要进行分组、映射和排序,您可以执行以下操作:

var occurrenceDay = function(occurrence){
    return moment(occurrence).startOf('day').format();
};

var groupToDay = function(group, day){
    return {
        day: day,
        times: group
    }
};

var result = _.chain(occurences)
    .groupBy(occurrenceDay)
    .map(groupToDay)
    .sortBy('day')
    .value();

16
投票

假设您的数据实际上是字符串,我不知道为什么您认为您需要这些库中的任何一个。您只是根据子字符串对字符串进行分组。

ES5引入了reduce,这对于积累东西来说非常有用:

创建日期数组的助手:

// Generate a dates array given a start date and how many to create:
function genDates(startDate, count) {
  var d = new Date(+startDate),
      dates = [d];
  for (var i=0; i<count; i++) {
    d = new Date(+d);
    d.setHours(d.getHours() + 10);
    dates.push(d);
  }
  return dates;
}

这个答案最初处理字符串,修改为与日期一起使用:

// Generate date key 'MMM dd'
// Replaces use of moment.js
function getDateKey(date) {
  var d = date.getDate();
  var m = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  return m[date.getMonth()] + ' ' + ((d<10?'0':'') + d);
}

// Generate an array in format [{day:'MMM dd', times:[d0, d1, ...]}, ...]
// Replaces use of underscore.js
var obj = dates.reduce(function(acc, d) {
            var p = getDateKey(d)
            if (!acc[0].hasOwnProperty(p)) acc[0][p] = [];
            acc[0][p].push(d);
            return acc;
          },[{}])
          .reduce(function(acc, v){
            Object.keys(v).forEach(function(k){acc.push({day:k, times:v[k]})});
            return acc;
          },[]);

console.log(JSON.stringify(obj));

如果最佳性能是关键,对于 5 到 100 个日期的数组,上述方法比下划线 + Moment 解决方案快 20 倍。为了使其更快,请删除所有迭代器和库的使用,并使用带有 for 循环的单个函数。请注意,上面只比使用 Moment.js 和 underscore.js 的解决方案长一行代码。


3
投票

如果您还需要按或(和)与日进行分组 - 我建议使用我的解决方案。

在上面的答案中,如果您在同一天得到不同的月份或年份 - 您的分组将不正确。

看看好的解决方案

_.groupBy(arrayOfDates, function (el) {
      return (el.getFullYear() + '|y|') + (el.getMonth() + '|m|') + (el.getDate() + '|d|');
    });

我在这里做什么?只需为每个日期创建一个唯一的键,其中包括:年、月和日。然后我通过这个唯一的键对数组进行分组。

result


0
投票

ES2024

您现在可以使用内置的

Object.groupBy()
Map.groupBy()
:

const randomHour = () => Math.floor(23 * Math.random());

const occurrences = [
  new Date(2024, 0, 1, randomHour()),
  new Date(2024, 0, 2, randomHour()),
  new Date(2024, 0, 1, randomHour()),
  new Date(2024, 2, 1, randomHour()),
];

const result = Object.groupBy(
  occurrences,
  (date) => `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
);

console.log(JSON.stringify(result, null, 4));
/* Stackoverflow: show only console */
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

浏览器支持还可以(截至 2024 年 2 月 15 日约为 82%),但还不是很好。

显然,如果您不想按年份分组,只需将该部分从您创建的唯一密钥中删除即可。


-6
投票

为什么需要这样的优化?如果您的数组不太大,那么您可能不需要优化算法。

我不熟悉给定的 js 库,但是您可以通过一个循环按天对数组进行分组。但是您需要以某种方式确定数组中的当前日期,然后使用日期字段和时间数组字段创建相应的 js 对象,然后将此对象添加到结果数组中。 如果在实现此算法之前对数组进行预排序,速度会快得多。

© www.soinside.com 2019 - 2024. All rights reserved.