Home > Back-end >  GroupingBy a range dates
GroupingBy a range dates

Time:09-23

I have a situation like this:

List<ObjectA> collapsed = list.stream().collect(Collectors.collectingAndThen(
        Collectors.groupingBy(
                ObjectA::getDate,
                Collectors.maxBy(Comparator
                        .comparing(ObjectA::getPriority1)
                        .thenComparing(ObjectA::getPriority2)
                        .thenComparing(ObjectA::getDate)
                        .thenComparing(ObjectA::getId))),
        map -> map.values().stream()
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList())));

and it works perfectly for the same dates. I just need to group items for a certain date window:

Integer daysWindow = 90;

so i want everything to happen for the dates in this window. how would this be possible?

Thanks to all!

CodePudding user response:

You can apply the filter in the beginning as shown below keeping the rest of the things the same.

LocalDate today = LocalDate.now();

list.stream().filter(obj -> !obj.getDate().isAfter(today.plusDays(90)))
    .collect(Collectors.collectingAndThen(....

Note: I assume ObjectA::getDate is of type java.time.LocalDate.

Demo:

import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        // Test
        System.out.println(inRange(LocalDate.now()));
        System.out.println(inRange(LocalDate.of(2021, 12, 31)));
        System.out.println(inRange(LocalDate.of(2022, 2, 20)));
    }

    static boolean inRange(LocalDate date) {
        return !date.isAfter(LocalDate.now().plusDays(90));
    }
}

Output:

true
false
false

ONLINE DEMO

Learn more about the modern Date-Time API* from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8 APIs available through desugaring and How to use ThreeTenABP in Android Project.

CodePudding user response:

As another approach, if what you're looking for is a map of date ranges to the corresponding values, you can give it a bit more work by creating a range data type, then generating groups on that type. Here's an example:

class DateRange {
    private final LocalDate from;
    private final int length;

    public DateRange(LocalDate from, int length) {
        super();
        this.from = from;
        this.length = length;
    }

    //getters

    @Override
    public int hashCode() {
        return this.from.hashCode(); //TODO
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof DateRange))
            return false;
        
        DateRange r = (DateRange) obj;

        return this.length == r.length && this.from.equals(r.from);
    }

    @Override
    public String toString() {
        return "("   from   " to "   from.plusDays(length)   ")";
    }
}

Then your stream can create a mapping, just as the one you currently have, but grouping on date ranges, after determining the range under which each element falls.

int daysWindow = 60;
var start = LocalDate.of(2021, 1, 1);

//example date list
List<LocalDate> dates = List.of(start.plusDays(20), 
        start.plusDays(30), start.plusDays(40), start.plusDays(50),
        start.plusDays(60), start.plusDays(70), start.plusDays(80));

var ranges = dates.stream()
        .collect(Collectors.groupingBy(d -> {
            var diff = ChronoUnit.DAYS.between(start, d);
            return new DateRange(start.plusDays(diff - diff % daysWindow), daysWindow);
        }));

Of course, you'll use your current collector, I just wanted to show the call to groupingBy, which you can still invoke with a downstream collector.

The one thing you need to determine is the value of start. I assume this can be set to the min date in the collection, although it can also be user-defined.


The output of the above code is as follows:

{(2021-03-02 to 2021-05-01)=[2021-03-02, 2021-03-12, 2021-03-22], 
 (2021-01-01 to 2021-03-02)=[2021-01-21, 2021-01-31, 2021-02-10, 2021-02-20]}

And when tested with a different window, int daysWindow = 90;:

{(2021-01-01 to 2021-04-01)=[2021-01-21, 2021-01-31, 2021-02-10, 
                             2021-02-20, 2021-03-02, 2021-03-12, 2021-03-22]}
  • Related