I have a list of LocalDates lets say 14-06-2020, 15-06-2020, 17-06-2020, 19-06-2020, 20-06-2020, 21-06-2020 and I want to have all consecutive intervals from above dates. So the output would be like
Interval 1 = [14-06-2020, 15-06-2020]
Interval 2 = [17-06-2020, 17-06-2020]
Interval 3 = [19-06-2020, 21-06-2020]
What would be the most efficient way to do in Java
So I have create an Interval class that would hold start and enddate
public class Interval{
private LocalDate startDate;
private LocalDate endDate;
}
I can iterate over each element in list of dates and then check for logic if two dates are consecutive something in below line,
public static void main(String args[]){
List<LocalDate> dates = new ArrayList<>();
dates.add(LocalDate.of(2020,6,14));
dates.add(LocalDate.of(2020,6,15));
dates.add(LocalDate.of(2020,6,17));
dates.add(LocalDate.of(2020,6,19));
dates.add(LocalDate.of(2020,6,21));
dates.add(LocalDate.of(2020,6,20));
Collections.sort(dates);
//Handle if empty or null
List<Interval> intervals = new ArrayList<>();
if(dates==null || dates.size()==0){
throw new IllegalArgumentException("list cannot be empty");
}
//If only one date then the interval starts and ends with same date
if(dates.size()==1){
Interval interval = new Interval();
interval.setStartDate(dates.get(0));
interval.setEndDate(dates.get(0));
}
LocalDate firstDate = dates.get(0);
for(int i =1;i<dates.size(); i++){
LocalDate endDate = dates.get(i);
LocalDate nextDate = endDate.plusDays(1);
//iterate over to get the desired list of interval
while(my condition satisfies){
//create new interval
}
//intervals.add(interval
}
}
I wanted to check if there is something better using stream api or can i group dates by consecutive days and then collect them using interval
Java 8 Object Oriented Programming Programming To check to find whether a given array contains three consecutive dates: Convert the given array into a list of type LocalDate. Using the methods of the LocalDate class compare ith, i+1th and i+1th, i+2th elements of the list if equal the list contain 3 consecutive elements.
To check to find whether a given array contains three consecutive dates: Convert the given array into a list of type LocalDate. Using the methods of the LocalDate class compare ith, i+1th and i+1th, i+2th elements of the list if equal the list contain 3 consecutive elements.
Java – Get All Dates Between Two Dates. 1 1. LocalDate.datesUntil () – Java 9. LocalDate ‘s datesUntil () method returns a sequential ordered stream of dates. The returned stream starts from ... 2 2. Stream.iterate () – Java 8. 3 3. Create dates in while loop – Java 7.
LocalDate ‘s datesUntil () method returns a sequential ordered stream of dates. The returned stream starts from startDate and goes to endDate (exclusive) by an incremental step of 1 day. 2. Stream.iterate () – Java 8 To get all dates, create a stream of dates adding adding 1 to startdate and so on, until end date.
A "workaround" towards an approach based on Stream
upon the sorted collection could be to use markers for range lookup -
List<Integer> rangeMarkers = new ArrayList<>();
rangeMarkers.add(0);
rangeMarkers.addAll(IntStream.range(0, dates.size() - 1)
.filter(i -> !dates.get(i).plusDays(1).equals(dates.get(i + 1)))
.mapToObj(i -> i + 1)
.collect(Collectors.toList()));
rangeMarkers.add(dates.size());
System.out.println(rangeMarkers);
and then use those markers to map dates to Interval
-
List<Interval> intervals = IntStream.range(0, rangeMarkers.size() - 1)
.mapToObj(i -> new Interval(dates.get(rangeMarkers.get(i)),
dates.get(rangeMarkers.get(i + 1) - 1)))
.collect(Collectors.toList());
System.out.println(intervals);
Stream API isn't your friend in this case. you can do it as I displayed the below but I think it isn't readable.
without non-stream API makes it more readable.
After sorting the list loop over the list by skipping the first index. current localDate
is the first element in the list. by checking its equality with the other elements change the current value and merge intervalMap
as you see. for current localDate in the loop if equality isn't matched put it on the map with a new key. (++index
). because you just want to have start localDate
and end localDate in the merge function I just set the endLocalDate of the first interVal
with the second interval endLocalDate value.
LocalDate current = dates.get(0);
Map<Integer, Interval> intervalMap2 = new HashMap<>();
int index = 1;
intervalMap2.put(1, new Interval(current,current));
for (LocalDate localDate : dates.subList(1, dates.size())) {
if (current.plusDays(1).equals(localDate)) {
current = localDate;
intervalMap2.merge(index, new Interval(localDate,localDate),
(val, val2) -> {val.setEndDate(val2.getEndDate());return val; });
} else {
intervalMap2.merge(index, new Interval(current,current),
(val, val2) -> {val.setEndDate(val2.getEndDate());return val; });
intervalMap2.put(++index, new Interval(localDate,localDate));
current = localDate;
}
}
however, if you interested in do it with stream version you can do like:
Map<Integer, Interval> intervalMap = dates.stream().sorted()
.collect(HashMap::new, (hashMap, localDate) -> {
if (hashMap.get(hashMap.size()) != null &&
hashMap.get(hashMap.size()).getEndDate()
.equals(localDate.minusDays(1))) {
hashMap.merge(hashMap.size(),
new Interval(localDate, localDate),
(val, val2) -> {val.setEndDate(val2.getEndDate());return val; });
} else {
if (hashMap.size() > 1)
hashMap.merge(hashMap.size(), hashMap.get(hashMap.size()),
(val, val2) -> { val.setEndDate(val2.getEndDate());return val;});
hashMap.put(hashMap.size() + 1, new Interval(localDate, localDate));
}
}, HashMap::putAll);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With