How to Apply Multiple Predicates to a Java.Util.Stream

How to apply multiple predicates to a java.util.Stream?

I am assuming your Filter is a type distinct from java.util.function.Predicate, which means it needs to be adapted to it. One approach which will work goes like this:

things.stream().filter(t -> filtersCollection.stream().anyMatch(f -> f.test(t)));

This incurs a slight performance hit of recreating the filter stream for each predicate evaluation. To avoid that you could wrap each filter into a Predicate and compose them:

things.stream().filter(filtersCollection.stream().<Predicate>map(f -> f::test)
.reduce(Predicate::or).orElse(t->false));

However, since now each filter is behind its own Predicate, introducing one more level of indirection, it is not clear-cut which approach would have better overall performance.

Without the adapting concern (if your Filter happens to be a Predicate) the problem statement becomes much simpler and the second approach clearly wins out:

things.stream().filter(
filtersCollection.stream().reduce(Predicate::or).orElse(t->true)
);

Java Stream filter match multiple criteria predicate

Try this out,

List<Person> filteredPeople = persons.stream()
.filter(p -> p.getAge() > 30)
.filter(p -> p.getAge() < 32)
.filter(p -> p.getHasComputer())
.limit(5).collect(Collectors.toList());

Notice that you may add additional filter predicates as needed. This is just a template to get your work done.

Or else if you have some dynamic number of Predicates passed by some external client you can still do it like so.

Predicate<Person> ageLowerBoundPredicate = p -> p.getAge() > 30;
Predicate<Person> ageUpperBoundPredicate = p -> p.getAge() < 32;
Predicate<Person> hasComputerPred = p -> p.getHasComputer();
List<Predicate<Person>> predicates = Arrays.asList(ageLowerBoundPredicate, ageUpperBoundPredicate,
hasComputerPred);
List<Person> filteredPeople = persons.stream()
.filter(p -> predicates.stream().allMatch(f -> f.test(p)))
.limit(5).collect(Collectors.toList());

Java Streams: Find first for multiple filter predicates

How about doing it the other way around? Streaming the products, and applying predicates on them.

List<Predicate<Product>> predicates = getPredicates();
List<Product> products = getProducts();
List<Product> filtered = products.stream().filter(product -> {
Iterator<Predicate<Product>> iterator = predicates.iterator();
while (iterator.hasNext()) {
Predicate<Product> currentPredicate = iterator.next();
if (currentPredicate.test(product)) {
iterator.remove();
return true;
}
}
return false;
}).collect(Collectors.toList());

The downside is you have to be careful which collection you use for predicates, Iterator.remove is not always supported.

Edit: Looks like i wasn't reading carefully enough. I think getting one of each would be best with loop.

List<Product> products = getProducts();
List<Predicate<Product>> predicates = getPredicates();
List<Product> matchingProducts = new ArrayList<>(predicates.size());
for (Product product : products) {
if (predicates.isEmpty()) {
break;
}
for (int predicateIndex = 0; predicateIndex < predicates.size(); predicateIndex++) {
Predicate<Product> predicate = predicates.get(predicateIndex);
if (predicate.test(product)) {
matchingProducts.add(product);
predicates.remove(predicateIndex);
break;
}
}
}

Actually managed to achieve it with a stream and takeWhile, you were correct, Benjamin.

List<Predicate<Product>> predicates = getPredicates();
List<Product> products = getProducts();
List<Product> matches = products.stream()
.takeWhile(product -> !predicates.isEmpty())
.filter(product -> {
Iterator<Predicate<Product>> iterator = predicates.iterator();
while (iterator.hasNext()) {
if (iterator.next().test(product)) {
iterator.remove();
return true;
}
}
return false;
})
.collect(Collectors.toList());

Just make sure takeWhile is before filter, otherwise last matching element gets skipped.

How to combine all predicates from ListPredicateMyClass

You can stream and reduce them like this:

Predicate<TaskFx> predicate = predicates.stream()
.reduce(x -> true, Predicate::and);

Apply multiple filter to a map in Java

There is Predicate#or(Predicate) to logically compose two Predicates.

Predicate<Student> livesInDelhi = student -> "delhi".equalsIgnoreCase(student.getAddress());
Predicate<Student> livesInAmsterdam = student -> "amsterdam".equalsIgnoreCase(student.getAddress());
Predicate<Student> livesInNewYork = student -> "new york".equalsIgnoreCase(student.getAddress());

Predicate<Student> livesInAnyOfTheseThreeCities = livesInDelhi.or(livesInAmsterdam).or(livesInNewYork);

A filter call would look like

.filter(e -> livesInAnyOfTheseThreeCities.test(e.getValue()))

How could I adapt the fourth lines where you're chaining filtering parameters?

Assuming we have an array of cities

final String[] cities = {"delhi", "amsterdam", "new york"};

for each Student, we could write a Predicate<Student> and reduce them by Predicate::or

Predicate<Student> livesInAnyOfGivenCities = 
Arrays.stream(cities)
.map(city -> (Predicate<Student>) student -> city.equalsIgnoreCase(student.getAddress()))
.reduce(Predicate::or)
.orElseGet(() -> student -> false);

student -> false is used when there are no cities given.

How to apply multiple Filters on Java Stream?

I suppose you want to keep all the TestObjects that satisfy all the conditions specified by the map?

This will do the job:

List<TestObject> newList = list.stream()
.filter(x ->
filterMap.entrySet().stream()
.allMatch(y ->
x.getProperty(y.getKey()) == y.getValue()
)
)
.collect(Collectors.toList());

Translated into "English",

filter the list list by keeping all the elements x that:

  • all of the key value pairs y of filterMap must satisfy:

    • x.getProperty(y.getKey()) == y.getValue()

(I don't think I did a good job at making this human readable...) If you want a more readable solution, I recommend Jeroen Steenbeeke's answer.

Java 8 Streams - Filter More Than One Condition

Simple enough

resultList.stream()
.filter(fixture -> fixture.getHome().equals(team) || fixture.getAway().equals(team)))
.collect(toList());

EDIT: This is on the assumption that order does not matter to you. If your final list needs to have home result and then away, have a look at Elliott Frisch's answer.

Apply few Predicates to stream filter in groovy

Predicate<SystemRunlife> filter = { systemRl -> true }
for (Predicate<SystemRunlife> systemRunlifePredicate : filterCollection) {
filter = (filter & systemRunlifePredicate)
}

I had combined predicates that way. Maybe someone can propose more convineint way.

How to use stream to apply multiple filters and convert to a List

For the next question, please provide real code instead of incomplete, typo ridden pseudo code.


As you need the complete list of As for step 3, you have to collect any potential stream from step 2 into a list anyway. Step 2 and 4 can be simplified by using streams, though:

public static List<A> makeListfromGroup(Map<Object, List<A>> m, Predicate<A> condition)
{
return m.values()
.stream()
.map(as -> as.stream().filter(condition).findAny().orElse(null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

For step 4, you can read how to apply multiple conditions at once here:

public static List<B> applyFilter(List<B> list, List<Predicate<B1>> filtreB1, List<Predicate<B2>> filtreB2)
{
return list.stream()
.filter(b -> filtreB1.stream().allMatch(p -> p.test(b.getB1())))
.filter(b -> filtreB2.stream().allMatch(p -> p.test(b.getB2())))
.collect(Collectors.toList());
}


Related Topics



Leave a reply



Submit