How to group by 15 minutes interval a json collection in Javascript
Asked Answered
D

2

5

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});
});
Dewittdewlap answered 30/5, 2014 at 22:4 Comment(10)
I'm a fan of underscore for work like this. They have a groupBy function that should help. underscorejs.org/#groupByMaudmaude
The question is a little bit ambiguous. Do the dates 19:00:00, 19:10:00 and 19:20:00 all belong to the same group, or should the interval between the first and the last date of a group be less than 15 minutes?Rubberneck
The first and the last date of a group be less than 15 minutes.Argil
@StéphanLascar: When does time start? Is it every 15 minutes from the earliest time in the list, or 15 minutes from the top of the hour, or something different?Wamsley
@Cory: The time start from the earliest time in the list.Argil
@StéphanLascar In that case, do you realize that the answer is not unique? Again, if you look at the dates 19:00:00, 19:10:00 and 19:20:00, how would you group them? Is the order in which the dates are provided relevant?Rubberneck
You should be able to figure this out with a little effort. Here's a really verbose example to get you started -> jsfiddle.net/PM3v9/1Subalternate
@adeneo: That's pretty nice.Wamsley
It's just an example of how to do this, which is why I didn't post it as an answer, it's not really clear to me what the sorting rules should be, but this will give you the result you wanted for that particular data, but what should happen if you have ten times with five minute intervals, should all of them end up in the same array or should some sort of initial time be used etc. It's all very unclear !Subalternate
possibly relevant, by year instead of by 15 min interval: #9272210Tusker
T
7

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
Tusker answered 30/5, 2014 at 23:47 Comment(0)
B
0

This method takes startDate,EndDate and interval to break DateTime and return DateTime Collection on specified interval

   const createDateTimeInterval = (startDate, endDate, interval) => {
        const dateArray = [];
        let currentDate = new Date(startDate);
        while (currentDate < new Date(endDate)) {
            var date = new Date(currentDate);
            let dateinString;
            if (date.getHours() < 12) {
                dateinString = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':' + (date.getMinutes() === 0 ? date.getMinutes() + '0' : date.getMinutes()) + ' AM';
            }
            else {
                dateinString = ((date.getHours() - 12) < 10 ? '0' + (date.getHours() - 12) : (date.getHours() - 12)) + ':' + (date.getMinutes() === 0 ? date.getMinutes() + '0' : date.getMinutes()) + ' PM';
            }
            dateArray.push({ label: dateinString, value: JSON.stringify(new Date(currentDate)) });
            currentDate = new Date(currentDate.getTime() + interval);
        }
    
        return dateArray;
    }
    const currentDate = new Date(new Date().getFullYear() + '-' + new Date().getMonth() + '-' + new Date().getDate())
    const nextDate = new Date(new Date(currentDate).getTime() + 60000 * 60 * 24)

This is the 15 minutes interval on a current date.

var dateOptions = createDateTimeInterval(currentDate, nextDate, (60000 * 15));
Bumblebee answered 5/9, 2021 at 18:14 Comment(1)
Please provide additional details in your answer. As it's currently written, it's hard to understand your solution.Adkins

© 2022 - 2024 — McMap. All rights reserved.