Массив дат JS как группировать по дням

Я пытаюсь выяснить наиболее оптимальный и с минимальным количеством циклов способ группировки моего массива объектов JS dates из этого: (обратите внимание, что это вывод консоли браузера, это actully реальные даты 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)]

для oragnized массива с каждой датой того же дня внутри "куска", чтобы я мог отобразить его в пользовательском интерфейсе "Aug 08" и показать 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)

и после того, как я получил уникальные дни еще один цикл, чтобы добавить тот же день в эту группу, как вы можете видеть сейчас, вы можете понять, почему мне это не нравится, и именно поэтому я хотел бы получить умную помощь, потому что ничего не приходит мне в голову с этим. Спасибо.

4 ответов


подчеркивание имеет _.метода groupBy функция, которая должна делать именно то, что вы хотите:

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

это вернет объект, где каждый ключ-день, а значение-массив, содержащий все вхождения за этот день.

чтобы преобразовать объект в массив той же формы, что и в вопросе, вы можете использовать map:

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();

предполагая, что ваши данные на самом деле строки, я не знаю, почему вы думаете, что вам нужна любая из этих библиотек. Вы просто группируете строки на основе подстрок.

в ES5, представил уменьшить, который отлично подходит для накопления вещей:

помощник для создания массива дат:

// 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));

Если оптимальное представление ключ, то выше в 20 раз быстрее чем решение подчеркивания + момента для массива от 5 до 100 дат. Чтобы сделать это быстрее, удалите все итераторы и библиотеки и используйте одну функцию с циклами for. Обратите внимание, что выше только одна строка кода длиннее, чем решение с использованием Moment.js и подчеркивание.js.


Если вам нужно группировать также по год или (и) месяц С днем - я рекомендую использовать мое решение.

в ответах выше, если вы получите другой месяц или год с тем же днем - ваша группировка будет неправильной.

посмотреть хорошим решением:

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

что я здесь делаю? Просто создайте уникальные ключи для каждой даты, которая включает в себя: год, месяц и день. И затем я группирую массив по этому уникальный ключ.

результат


зачем нужна эта оптимизация? Если Ваш массив не большой enoguh, чем вам, вероятно, не нужно оптимизировать свой алгоритм.

Я не знаком с данными библиотеками js, но вы можете сгруппировать свой массив по дням с одним циклом. Но вам нужно как-то определить текущий день в массиве, а затем создать соответствующий объект js с полем day-field и полем times-array, а затем добавить этот объект в результирующий массив. Это будет намного быстрее, если вы отсортировать массив, прежде чем реализация данного алгоритма.