I have a list of objects which hold multiple properties, one of which is a LocalDate. I'd like to find the object with the most recent date out of this list.
I'm fairly green with Java 8 and using streams. Like most programming, it seems as though there's more than one way to skin this cat. Here's what I have so far.
list.stream().filter( object -> object.getId() == id
&& object.getCancelDate() == null
&& object.getEarliestDate() != null)
.min( Comparator.comparing( LocalDate::toEpochDay )
.get();
This gives me "Non-static method cannot be referenced from a static context" for the Comparator function.
I've looked at possibly creating a map of just the dates from the filtered objects as well and have so far come up with something like this.
list.stream().filter( object -> object.getId() == id
&& object.getCancelDate() == null
&& object.getEarliestDate() != null)
.map( data -> data.getEarliestDate() )
.collect( Collectors.toList() )
and I'm not really sure where to go from there or if that will even work.
I know there's an easy way to do this but my brain just isn't connecting the dots.
Update
Thanks for the response. I updated my code
Optional<YourObject> recentObject = list.stream().filter(object ->
object.getId() == id && object.getCancelDate() == null &&
object.getEarliestDate() != null)
.max(Comparator.comparing(s -> s.getEarliestDate().toEpochDay()));
I now get a compiler error Incompatible types.
Required:Optional<MyClass>
Found:Optional<capture<? extends MyClass>>
The method does extend MyClass, so in the type declaration for Optional, do I need to do something like MyClass.class?
Update 2 Thanks to @Hogen for helping fix the compiler error by adding on the .map() at the end. Here's what it looked like after the change.
Optional<MyClass> date =
list.stream().filter(object -> object.getId() == id &&
object.getCancelDate() == null &&
object.getEarliestDate() != null)
.max(Comparator.comparing( s -> s.getEarliestDate()
.toEpochDay())).map(Function.identity());
However, I was able to come up with a solution after some help that moves the map to a different spot so that I wouldn't run into the issue of using an extended class.
Optional<LocalDate> mostRecentDate = list.stream()
.filter(data -> data.getId() == id && data.getCancelDate() == null)
.map(MyObject::getEarliestDate)
.filter(Objects::nonNull)
.max(LocalDate::compareTo);
map(u -> u. date) . max(Date::compareTo) . orElseThrow(() -> new IllegalArgumentException("Expected 'list' to be of size: >= 2.
You're mostly looking out for:
Optional<YourObject> recentObject = list.stream()
.filter(object -> object.getId() == id && object.getCancelDate() == null && object.getEarliestDate() != null)
.max(Comparator.comparing(YourObject::getEarliestDate)); // max of the date for recency
From LocalDate.compareTo
Compares this date to another date. The comparison is primarily based on the date, from earliest to latest. It is "consistent with equals", as defined by
Comparable
.
Putting this in an answer for visibility. Based on @nullpointer's answer and @Holger's suggestion I was able to come up with the two following solutions.
Solution 1
Optional<MyClass> mostRecentDate = list.stream()
.filter(myObject -> myObject.getId() == id &&
myObject.getCancelDate() == null && myObject.getEarliestDate() != null)
.max(Comparator.comparing( s -> s.getEarliestDate()
.toEpochDay())).map(Function.identity());
Solution 2
LocalDate mostRecentDate = list.stream()
.filter(myObject -> myObject.getId() == id && myObject.getCancelDate() == null)
.map(MyObject::getEarliestDate)
.filter(Objects::nonNull)
.max(LocalDate::compareTo)
.orElse(null);
Both solutions work but the second solution is cleaner in my opinion and less ambiguous. It removes the .map(Function.identity()) code that doesn't actually do anything as @Holgen pointed out while making use of Java 8's new Method Reference :: . It also filters the list of objects and maps the dates to a new list that then uses .max() with the compareTo() method instead of a custom function. I view the custom function and the useless code as messy and to anyone reading the code, might make it less understandable.
Note in the second solution I've also removed the Optional class. Using .orElse() satisfies returning the actual LocalDate class instead of an option from the stream.
Thanks for the help everyone and I hope this answer helps others.
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