Abhi
Abhi

Reputation: 71

Grouping Java 8. Inner grouping based on time interval from a given start time

I've the list in the following format:

int intervalInMinutes = 10;
String startTimeForGrouping = "2017-05-09T15:37:51.896+00:00";
List<MyObject> myObjList = Arrays.asList( 
    new MyObject("1","a","2017-05-09T15:38:51.896+00:00"),
    new MyObject("1","a","2017-05-09T16:41:51.896+00:00"),
    new MyObject("1","a","2017-05-09T16:49:51.896+00:00"),
    new MyObject("1","a","2017-05-09T16:51:51.896+00:00"),
    new MyObject("2","b","2017-05-09T17:38:51.896+00:00"),
    new MyObject("2","b","2017-05-09T18:41:51.896+00:00")
);

I've got the list in the above format: I wanted to iterate through that and perform some grouping with an interval from given start time and achieve as follows: In the above list, the group id 1 should return 2 lists.

First 2 objects in the single list because its in the first interval from startTimeForGrouping ("2017-05-09T15:37:51.896+00:00" + intervalInMinutes (10)).

3rd and 4th in a different list because that comes in the next interval ("2017-05-09T15:47:51.896+00:00" + intervalInMinutes (10)). The same logic should repeat for id 2 as well

I have tried this

Map<String, Map<Long, List<MyObject>>> aggrResult =
myObjList.stream()
    .collect(
        Collectors.groupingBy(
            MyObject::getId,
            Collectors.groupingBy(eachQueueObj -> eachQueueObj.getIntervalStartTime()/interval)));

but this is wrong because I want to aggregate based on the interval from the given start time.

Please help me since am new to java streaming

Upvotes: 3

Views: 649

Answers (2)

sk l
sk l

Reputation: 81

I think the code is that,you could try and remodify the getTime method to fix your need

 package com.test;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 *
 *
 *
 *
 *
 *
 * @author shikai.liu
 * @version 1.0
 * @since JDK1.7
 */
public class TestList {

    public static String strDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
    public static SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);

    public static class MyObject {
        public String id;
        public String code;
        public String time;

        public MyObject(String id, String code, String time) {
            this.code = code;
            this.id = id;
            this.time = time;
        }

        public String getId() {
            return this.id;
        }

        public String getIntervalStartTime() {
            return time;
        }

        public long getTime(Integer t, String srcTime) {
            long result = 0;
            try {

                Date dstDate = sdf.parse(time);
                Date srcDate = sdf.parse(srcTime);
                long inteval = dstDate.getTime() - srcDate.getTime();

                result = inteval / (1000 * 60) / t;
            } catch (Exception e) {
                e.printStackTrace();
            }

            return result;
        }
    }

    public static void main(String[] args) throws Exception {
        int intervalInMinutes = 10;
        String startTimeForGrouping = "2017-05-09T15:37:51.896+00:00";
        List<MyObject> myObjList = Arrays.asList(new MyObject("1", "a", "2017-05-09T15:38:51.896+00:00"), new MyObject("1", "a",
                "2017-05-09T16:41:51.896+00:00"), new MyObject("1", "a", "2017-05-09T16:49:51.896+00:00"), new MyObject("1", "a",
                "2017-05-09T16:51:51.896+00:00"), new MyObject("2", "b", "2017-05-09T17:38:51.896+00:00"), new MyObject("2", "b",
                "2017-05-09T18:41:51.896+00:00"));

        Map<String, Map<Long, List<MyObject>>> aggrResult = myObjList.stream().collect(
                Collectors.groupingBy(MyObject::getId, Collectors.groupingBy(a -> a.getTime(intervalInMinutes, startTimeForGrouping))));
        System.out.println(1);
    }
}

Upvotes: 1

Sweeper
Sweeper

Reputation: 271810

You should parse the date strings into Instant objects, and call Duration.between to get a duration. Then, divide that duration by intervalInMinutes minutes. Finally, multiply this number by intervalInMinutes and add that to startTimeForGrouping to get the grouping key.

Before the stream operation, parse startTimeForGrouping to an instant, and make a Duration from startTimeForGrouping:

Instant startInstant = OffsetDateTime.parse(startTimeForGrouping).toInstant();
Duration interval = Duration.ofMinutes(startTimeForGrouping);

Then you can declare a method roundInstant:

public static Instant roundInstant(Instant instant, Duration interval, Instant start) {
    long multiple =
            Duration.between(start, instant)
                .getSeconds() / interval.getSeconds();
    return start.plus(interval.multipliedBy(multiple));
}

The second groupingBy call can then look like this:

Collectors.groupingBy(
    eachQueueObj -> roundInstant(
        OffsetDateTime.parse(eachQueueObj.getIntervalStartTime()).toInstant(),
        interval,
        startInstant
    ).getEpochSecond()
)

In fact, I would recommend you to not use Strings and Longs, and work with Instants directly.

Upvotes: 3

Related Questions