Let say we have a collection like this in Javascript:
[
{ date: 'Fri, 02 May 2014 19:05:00 GMT', value:'abc' },
{ date: 'Fri, 02 May 2014 23:43:00 GMT', value:'jkl' },
{ date: 'Fri, 02 May 2014 19:01:00 GMT', value:'def' },
{ date: 'Fri, 02 May 2014 19:09:00 GMT', value:'ghi' },
{ date: 'Fri, 02 May 2014 23:54:00 GMT', value:'mno' }
]
I would like to to find an elegant algorithm to group by this array by "closest" date. If a date is 15 minutes before or after a previous date, it will be pushed in the same object.
I doesn't really matter how the sub-array will be structured. The result for this entry could be:
[
[
{ date: 'Fri, 02 May 2014 19:05:00 GMT', value:'abc' },
{ date: 'Fri, 02 May 2014 19:01:00 GMT', value:'def' },
{ date: 'Fri, 02 May 2014 19:09:00 GMT', value:'ghi' }
], [
{ date: 'Fri, 02 May 2014 23:43:00 GMT', value:'jkl' },
{ date: 'Fri, 02 May 2014 23:54:00 GMT', value:'mno' }
]
]
I have try without real success using underscore.js:
_.map(logs_by_date, function(group, date) {
return _.reduce(group, function(memo, elem) {
var moment = get_moment(elem);
memo[moment].push(elem);
return memo;
}, { 'date': date});
});
Starting with the UnderscoreJS code from tjdett for a group-by-year question:
var dateGroups = _.chain(objarray)
.groupBy(function(obj) { return obj.date.getFullYear(); })
.sortBy(function(v, k) { return k; })
.value();
You could try this same solution with a groupBy function designed for 15 minute intervals instead of years, with return Math.floor(+(obj.date)/(1000*60*15));
This return statement uses +
to convert the Date object to a number of milliseconds (since epoch), and then divides by 1000*60*15 for 15 minute intervals with Math.floor()
discarding the fraction.
For that to work, obj.date must be type Date. If your dates are just strings, you may first need to parse the year, month, day, hour, minutes out of those strings and then construct a new Date object.
This will create absolute 15 minute clock blocks, i.e. 01:00:00-01:14:59.9999, 01:15:00-01:29:59.9999; not 15 minutes that begin with new activity.
So if you want a group of 15 minutes of data that starts with new data, the groupBy function would need to be created with a closure retaining state of when the current group ends so that new groups could be started, and it would need to be fed from objects sorted by date, so that a sort needs to happen first.
That sounds Rube-Goldbergish, and might be easier to do directly like this (untested):
fixDates(objArray); // a function you'll write to turn your date strings into Date objects
function compareDates(a,b){ return (+a.date)-(+b.date); }
objArray.sort(compareDates); // sorts in place, array is changed
var groups = [], g=[], d=0;
for (var gx=+objArray[0].date+15*60*1000,i=0,l=objArray.length; i<l; ++i){
d = +(objArray[i].date);
if (d>gx){
groups.push(g);
g = [];
gx = +objArray[i].date+15*60*1000;
}
g.push(objArray[i]);
}
groups.push(g); // include last group otherwise unpushed
// result in groups