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

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);

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.

Related

Java Stream filter - how can i filter data without wrapping the filter code in 'if' condition for checking null on the filtering keys?

I am doing the following:
List<Objects> filtered = objects.stream()
.filter(o -> source.equals(o.getSource()) && date.equals(o.getDate()) && id.equals(o.getId()))
.collect(Collectors.toList());
where both date and id could possibiliy be null, as the are coming from method parameters.
How can I ignore them if null, without wrapping the above code in an if statement tp check 'id' and 'date' for null values ? I want to do it inside the filter.
Edit : To make it more clear, i want the filter to act only on the non-null values, i.e if date is non-null and id is null, then filter only on date and ignore id, and so on..
Thanks
An additional option is to do the null checks in the predicate:
.filter(o -> source.equals(o.getSource())
&& (null == date || date.equals(o.getDate()))
&& (null == id || id.equals(o.getId())))
You could use the static method Objects::equals. Chances are that this method is designed for just this:
List<Objects> filtered = objects.stream()
.filter(o -> Objects.equals(source, o.getSource()) && Objects.equals(date, o.getDate()) && Objects.equals(id, o.getId()))
.collect(Collectors.toList());
Note: as Scratte mentioned in the comments, this may not filter out objects for which getDate() returns null, if date is also null. Same goes for id. If that's the case, then the abovementioned code snippet does not comply. In that case, then we have to explicitly filter for non-null dates and ids:
.filter(o -> Objects.nonNull(o.getId()) && Objects.nonNull(o.getDate()))
Update
If you want to skip the comparison for getDate() if date is null (and same for id), then you could check first for null, just like in ernest_k's answer.
You could easily build a small method for it:
public static boolean nullOrEquals(Object baseObject, Object compareObject) {
return baseObject == null || Objects.equals(baseObject, compareObject);
}
.filter(o -> Objects.equals(source, o.getSource())
&& nullOrEquals(date, o.getDate())
&& nullOrEquals(id, o.getId()))
Here's an Ideone example.

Entity Framework: Any or All - Unable to create a constant value of type 'System.Collections.Generic.List`1'

I am trying to do something like this:
from t in ent.myEntities
where SelectedProperties == null || SelectedProperties.Any(le => le == t.Entity)
select t
basically trying to cover 2 cases. accepting an empty list, should return all entities, or filter on the list if it is supplied.
above actually does work when i supply the list, however in the case when it is null i get:
Unable to create a constant value of type
'System.Collections.Generic.List`1'. Only primitive types ('such as
Int32, String, and Guid') are supported in this context
also tried using this with a string array:
where arr == null || arr.Contains(t.Entity)
is it possible to have such a condition without having to build a predicate (which is a bigger effort)?
You might want to try using the list in a simpler way:
where SelectedProperties == null || SelectedProperties.Contains(t.Entity)
It may well not work, but it's worth a try. Otherwise, if this is really your whole query, I'd just write it as:
var query = SelectedProperties == null
? ent.myEntities
: ent.myEntities.Where(t => SelectedProperties.Contains(t.Entity));
EDIT: Okay, if you have to use Any, and have lots of these to compose, you can do it like this:
var query = ent.myEntities;
if (SelectedProperties != null)
{
query = query.Where(t => SelectedProperties.Any(x => x == t.Entity));
}
if (SomethingElse)
{
query = query.Where(...);
}
// etc
I'm using EF5, something like this will fix the issue:
ent.myEntities.ToList().Where(t => SelectedProperties == null || SelectedProperties.Contains(t.Entity));

Linq with Logic

I have simple Linq statement (using EF4)
var efCars = (from d in myentity.Cars
where d.CarName == inputCar.CarName
&& d.CarIdNumber == inputCar.IdNumber
&& d.Make == inputCar.Make
select d.Car);
I want it to be smarter so that it will only query across one or more of the 3 fields IF they have values.
I can do a test before, and then have a separate linq statement for each permutation of valyes for inputcar
(i.e. one for all 3, one for if only carname has a value, one for if carname AND CarIdNumber has a value etc etc)
but there must be a smarter way
Thanks!
If "has no value" means null then you can use the null coalescing operator ?? to say take the first value if populated, otherwise take the second:
var efCars = (from d in myentity.Cars
where d.CarName == (inputCar.CarName ?? d.CarName
&& d.CarIdNumber == (inputCar.IdNumber && d.CarIdNumber)
&& d.Make == (inputCar.Make && d.Make)
select d.Car);
This basically says if a value exists it must match, otherwise treat it as matching
However if instead you're saying "when a special value (empty string) ignore it, otherwise match" then you can do one of two approaches (or possibly more!):
where (inputCar.CarName == "" || d.CarName == inputCar.CarName)
where (string.IsNullOrEmpty(inputCar.CarName) || d.CarName == inputCar.CarName)
For performance (when dealing with database queries) it can sometimes be beneficial to let EF generate queries based on the filters, instead of using one generic query. Of course you will need to profile whether it helps you in this case (never optimize prematurely), but this is how it would look if you dynamically build your query:
var efCars =
from car in myentity.Cars
select car;
if (inputCar.CarName != null)
{
efCars =
from car in efCars
where care.CarName == inputCar.CarName
select car;
}
if (inputCar.IdNumber != null)
{
efCars =
from car in efCars
where care.CarIdNumber == inputCar.IdNumber
select car;
}
if (inputCar.Make != null)
{
efCars =
from car in efCars
where care.Make == inputCar.Make
select car;
}
where (inputCar.CarName != null || d.CarName == inputCar.CarName) &&...

TargetInvocationException thrown when attempting FirstOrDefault on IEnumerable

I suspect I'm missing something rather basic, yet I can't figure this one out.
I'm running a simple linq query -
var result = from UserLine u in context.Users
where u.PartitionKey == provider.Value && u.RowKey == id.Value
select u;
UserLine user = null;
try
{
user = result.FirstOrDefault();
}
For some reason this produces a TargetInvocationException with an inner exception of NullReferenceException.
This happens when the linq query produces no results, but I was under the impression that FirstOrDefault would return Default<T> rather than throw an exception?
I don't know if it matters, but the UserLine class inherits from Microsoft.WindowsAzure.StorageClient.TableServiceEntity
there are two possible reasons:
provider.Value
id.Value
Are you sure that theese nullables have value. You might want to check HasValue before
var result = from UserLine u in context.Users
where (provider.HasValue && u.PartitionKey == provider.Value)
&& (id.HasValue && u.RowKey == id.Value)
select u;
UserLine user = null;
try
{
user = result.FirstOrDefault();
}
I thought it produced a different error, but based on the situation in which the problem is occurring you might want to look to check if context.IgnoreResourceNotFoundException is set to false? If it is try setting it to true.
This property is a flag to indicate whether you want the storage library to throw and error when you use both PartitionKey and RowKey in a query and no result is found (it makes sense when you think about what the underlying REST API is doing, but it's a little confusing when you're using LINQ)
I figured it out - the problem occured when either id or provider had '/' in the value, which the id did. when I removed it the code ran fine
Understanding the Table Service Data Model has a section on 'Characters Disallowed in Key Fields' -
The following characters are not allowed in values for the
PartitionKey and RowKey properties:
The forward slash (/) character
The backslash () character
The number sign (#) character
The question mark (?) character
Here's some fun try putting the where query the other way around like this to see if it works (I heard a while ago it does!):
where (id.HasValue && u.RowKey == id.Value) && (provider.HasValue && u.PartitionKey == provider.Value)
Other than this you can now set IgnoreResourceNotFoundException = true in the TableServiceContext to receive null when an entity is not found instead of the error.
It's a crazy Azure storage thing.

Linq check to see is there any NULLs in a set of DataRows?

I have a set DataRows and I want to check if any of the fields in any of those rows has a NULL value in it. I came up with this below, but I'm not sure because I'm nesting an ALL.
result.AsEnumerable().AsQueryable().All(o => o.ItemArray.All(i=>i == DBNull.Value))
Hard to tell because I can't put a "watch" in lambdas.
Actually you need to use Any (in your code you will return true if All values are null) and AsQueryable() is useless in this case.
bool nullFound = result.AsEnumerable()
.Any(o => o.ItemArray.Any(i=>i == DBNull.Value || i == null));
Then, If you need a list of all rows with some value null, just do the following:
var rowsWithNulls = result.AsEnumerable()
.Where(o => o.ItemArray.Any(i=>i == DBNull.Value || i == null))
.ToList();
P.S.
I also added a null check to be more safe, but if you are sure to have only DBNull.Value, you can remove it.
Not sure if this is the correct answer. I'm also rather new to Linq, but i believe you can do something like this;
result.AsEnumerable().AsQueryable().SingleOrDefault(o => o.ItemArray.All(i=>i == DBNull.Value))
This will return an list of items or null if there aren't any. Not sure if you can also nest it, but don't see why it wouldn't be possible

Resources