Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find most recent date in a list of objects on LocalDate property using Java 8 stream

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);
like image 980
acousticarmor Avatar asked Dec 11 '18 15:12

acousticarmor


People also ask

How do I find the max date in a list of dates using Java?

map(u -> u. date) . max(Date::compareTo) . orElseThrow(() -> new IllegalArgumentException("Expected 'list' to be of size: >= 2.


2 Answers

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.

like image 64
Naman Avatar answered Oct 12 '22 17:10

Naman


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.

like image 41
acousticarmor Avatar answered Oct 12 '22 18:10

acousticarmor