Array Of JS Dates How To Group By Days

I'm trying to figure out the most optimal and with as minimum amount of loops way to group my array of js dates objects from this: (Take a note this is browser console output it's actully real JS dates like 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)]

to oragnized array with each date of the same day inside a "chunk" so I can display it on the UI "Aug 08" and show 2 or how many dates inside that day.

for example:

[{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)]}]

My current way I thought about doing it was

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

After that to get unique days:

_.uniq(startDays, true)

and after I got the unique days another loop to add the same day to this group as you can see by now you might see why I don't like it and this is why I would love to get some smart help because nothing gets to my head with this. Thank you.

Answers:

Answer

Underscore has the _.groupBy function which should do exactly what you want:

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

This will return an object where each key is a day and the value an array containing all the occurrences for that day.

To transform the object into an array of the same form as in the question you could use map:

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

To group, map and sort you could do something like:

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

Presuming your data is actually strings, I don't know why you think you need either of those libraries. You are just grouping strings based on substrings.

ES5 introduced reduce, which is great for accumulating things:

A helper to create an array of dates:

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

This answer originally dealt with strings, modified to work with 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));

If optimal performance is the key, the above is 20 times faster than the underscore + Moment solution for an array of 5 to 100 dates. To make it faster, remove all use of iterators and libraries and use a single function with for loops. Note that the above is only one line of code longer than the solution using Moment.js and underscore.js.

Answer

If you need to grouping also by year or (and) month with day - I recommend to use my solution.

In answers above if you'll get different month or year with the same day - your grouping will be incorrect.

Look at the good solution:

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

What I do here? Just create an unique keys for each date, which includes: year, month and day. And then I group an array by this unique key.

result

Answer

Why do need this optimization? If your array is not large enoguh than you probably don't need to optimize your algorithm.

I am not familiar with the given js libraries, but you can group your array by days with one loop. But you need to somehow determine the current day in the array and then create corresponding js object with a day-field and a times-array field and then add this object to your resulting array. It will be much faster if you presort your array before implementing this algorithm.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.