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

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.

Related

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

How to create a dynamic where clause using a name value collection?

I have column search filter values sent to my web api and I can't figure how to make the where clause dynamic?
Look below (too much code)!!
Name Value Collection:
public DataTablePager<AccountDTO> Get([FromUri] DataTableParameter param)
{
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
Casting the search values:
if (!String.IsNullOrEmpty(nvc["sSearch_0"]) && !int.TryParse(nvc["sSearch_0"], out tmpInt) ||
!String.IsNullOrEmpty(nvc["sSearch_1"]) && !int.TryParse(nvc["sSearch_1"], out tmpInt) ||
!String.IsNullOrEmpty(nvc["sSearch_10"]) && !int.TryParse(nvc["sSearch_10"], out tmpInt)
Setup Where Clause:
filteredresults = filteredresults.Where(i => CorrectNumericTypes
&& (Lead_ID == null || i.Lead_ID == Lead_ID)
&& (Account_ID == null || i.Account_ID == Account_ID)
This is a classic case for the Pipes and Filters Architectural pattern.
You can construct a list of filters based on dynamic (runtime) conditions. Or keep a static list of filters, the ones where there is no criteria, the data just passes through. Apply this list of filters on the data set or the Query.
I don't think you can achieve that with LINQ. You can design a query builder (using the same Filters pattern)
Here's what I needed (predicate): http://www.c-sharpcorner.com/UploadFile/c42694/dynamic-query-in-linq-using-predicate-builder/

How to pass an external parameter to LINQ where clause in CRM

I have a LINQ query which works fine as for stand alone lists but fails for CRM
var lst = new List<bool?>();
lst.Add(null);
lst.Add(true);
lst.Add(false);
bool IsWet = false;
var newlst = from exch_HideVoiceSignature in lst where
(((exch_HideVoiceSignature!=null && exch_HideVoiceSignature==false
|| exch_HideVoiceSignature== null) )&& !IsWet) select exch_HideVoiceSignature;
newlst.Dump();
var question = from q in exch_questionSet where ((q.exch_HideVoiceSignature != null
&& q.exch_HideVoiceSignature.Value == 0 )|| q.exch_HideVoiceSignature == null )
&& !IsWet select q.exch_HideVoiceSignature;
question.FirstOrDefault().Dump();
As you can see I can pass the variable IsWet to LINQ query for a standard list fine and get values for first list. But when I execute the same for second list, I get the following error
Invalid 'where' condition. An entity member is invoking an invalid property or method
The CRM LINQ provider won't support the evaluation you attempting. It only supports evaluation of where criteria is evaluating an entity field.
That's not a problem. Since you want the LINQ query to only use the where clause if IsWet is false (correct me if I'm wrong on that.) So we simply do the evaluation to determine if the where clause should be added or not. Then execute your query.
var question = from q in exch_questionSet
select q.exch_HideVoiceSignature;
if (!IsWet)
{
question.Where(x => ((x.exch_HideVoiceSignature != null
&& x.exch_HideVoiceSignature.Value == 0) || x.exch_HideVoiceSignature == null));
}
question.FirstOrDefault().Dump();
I am constantly confronted with that problem.
Try to "detach" (for example call .ToArray()) your query (while it is "clear") from CRM and then filter query using external parameter. This should help.
var question =
(from q in exch_questionSet
where (
(q.exch_HideVoiceSignature != null && q.exch_HideVoiceSignature.Value == 0 ) ||
q.exch_HideVoiceSignature == null )
select q.exch_HideVoiceSignature
).ToArray().Where(q => !IsWet);
question.FirstOrDefault().Dump();
UPDATE
If you are using IsWet flag to control blocks of conditions (enable and disable them from the one point in the code) then probably you may be interested in class named PredicateBuilder which allows you to dynamically construct predicates.
Just because I had an existing query with lot of other joins etc. and I wanted to pass this additional parameter to it I ended up using a var statement which dumps the rows to a list and applies the where clause in the same statement
bool IsWet =true ;
var question = ...existing query ...
select new {
...existing output ...,
Wet =q.exch_HideVoiceSignature != null &&
q.exch_HideVoiceSignature.Value == 119080001,
Voice = q.exch_HideVoiceSignature == null ||
(q.exch_HideVoiceSignature != null &&
q.exch_HideVoiceSignature.Value == 119080000) ,
}
;
var qq = IsWet ?
question.ToList().Where(X=> X.Wet ) :
question.ToList().Where(X=> X.Voice );
qq.FirstOrDefault().Dump();

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

Unpassable Where Clauses LINQ-to-SQL

As I'm struggling to learn LINQ I’ve managed to generate a SQL statement with "AND (0 = 1)" as part of the where clause. I'm just wondering if this result is common in poorly written queries and is a known issues to try and avoid or if I am doing something totally backwards to end up with this.
Update
public static IEnumerable<ticket> GetTickets(stDataContext db,string subgroup, bool? active)
{
var results = from p in db.tickets
where
( active == null || p.active == active )
/*(active == null ? true :
((bool)active ? p.active : !p.active))*/ &&
p.sub_unit == db.sub_units.Where(c=>subgroup.Contains(c.sub_unit_name))
select p;
return results;
}
If I ignore the active part and just run
public static IEnumerable<ticket> GetTickets1(stDataContext db,string subgroup, bool? active)
{
return db.tickets.Where(c => c.sub_unit.sub_unit_name == subgroup);
}
It returns the groups of tickets I want ignoring the active part.
I'd pull the processing out of the ternary operators.
where ( active == null || p.active == active )
EDIT
The rest of the where clause looks funky too... why is it not just doing
&& p.sub_unit.sub_unit_name == subgroup
or
&& subgroup.Contains(p.sub_unit.sub_unit_name)
?
That is some pretty heavy abuse of the ternary operator.
This expression:
(active == null ? true :
((bool)active ? p.active : !p.active))
Is equivalent to the following logic:
bool result;
if (active == null)
{
result = true;
}
else
{
if ((bool)active)
{
result = p.active;
}
else
{
result = !p.active;
}
}
result &= ...
Think carefully about what this is doing:
If active is null, you're fine, it skips to the next condition.
If active is true, result is true at the end of the conditional.
If active is false, result is false at the end of the conditional.
In the last case, the query can never return any rows!
#Tanzelax has already supplied a simple rewrite. The main idea is that you want to compare p.active to active, not actually evaluate the condition as p.active.
This is probably caused by a null value in one you the columns you have declared as non-nullable. LINQ2SQL makes all columns non-nullable by default. Go back to the designer and change the fields to allow values to be null. Unfortunately this feature is By Design (see connect.microsoft.com link below.)
(linq) incorrect sql generated for row.column == localVar when localVar is null (should be "is null" check)

Resources