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]}