Reputation: 710
I have a List of LocalDate objects (dates
) and I want to find the entry in the list that is closest to a given date (milestoneDate
).
Closest means that the date could be before or after milestoneDate
but it has the least difference. For example something the day before milestoneDate
would be closer than something 2 days after milestoneDate
.
So far the best idea I have is to convert the values in dates
toEpochSecond and then get the absolute value for the difference between that value and the toEpochSecond
of milestoneDate
and select the min value.
long milestoneSeconds = milestoneDate.atStartOfDay().toEpochSecond(ZoneOffset.UTC);
return dates.stream().sorted((x, y) -> {
long xDiff = Math.abs(x.atStartOfDay().toEpochSecond(ZoneOffset.UTC) - milestoneSeconds);
long yDiff = Math.abs(y.atStartOfDay().toEpochSecond(ZoneOffset.UTC) - milestoneSeconds);
return (int) (xDiff - yDiff);
}).findFirst();
While I suspect this will work, it feels a bit heavy. Is there a more elegant way to do this?
Upvotes: 1
Views: 1013
Reputation: 271625
You can find the number of days between the milestone date and each of the dates in the list directly. There is no need to convert to an epoch second.
dates.stream().min(Comparator.comparingLong(
x -> Math.abs(ChronoUnit.DAYS.between(x, milestoneDate))
));
Note that there can be cases where there are 2 dates that are equally close to the milestone date - one before it and one after it. If you have a preference of which one to choose in this situation, you can add a thenComparing
to the comparator:
// gets the date before
dates.stream().min(Comparator.<LocalDate>comparingLong(
x -> Math.abs(ChronoUnit.DAYS.between(x, milestoneDate))
).thenComparing(Comparator.naturalOrder()));
// gets the date after
dates.stream().min(Comparator.<LocalDate>comparingLong(
x -> Math.abs(ChronoUnit.DAYS.between(x, milestoneDate))
).thenComparing(
Comparator.<LocalDate>naturalOrder().reversed()
));
It's a bit unfortunate that Java can't infer the type parameters :(
Upvotes: 2