I have to filter a Collection of Objects by a Map, which holds key value pairs of the Objects field names and field values. I am trying to apply all filters by stream().filt
A more general approach is to create a multi filter (Predicate
) which is concatenated using Predicate.and(...)
or Predicate.or(...)
. This is applicable to anything using Predicate
- first of all Stream
and Optional
.
Since the result is a Predicate
itself one can continue with Predicate.and(...)
or Predicate.or(...)
or with building more complex predicates using MultiPredicate
again.
class MultiPredicate {
public static Predicate matchingAll(Collection> predicates) {
Predicate multiPredicate = to -> true;
for (Predicate predicate : predicates) {
multiPredicate = multiPredicate.and(predicate);
}
return multiPredicate;
}
@SafeVarargs
public static Predicate matchingAll(Predicate first, Predicate... other) {
if (other == null || other.length == 0) {
return first;
}
Predicate multiPredicate = first;
for (Predicate predicate : other) {
multiPredicate = multiPredicate.and(predicate);
}
return multiPredicate;
}
public static Predicate matchingAny(Collection> predicates) {
Predicate multiPredicate = to -> false;
for (Predicate predicate : predicates) {
multiPredicate = multiPredicate.or(predicate);
}
return multiPredicate;
}
@SafeVarargs
public static Predicate matchingAny(Predicate first, Predicate... other) {
if (other == null || other.length == 0) {
return first;
}
Predicate multiPredicate = first;
for (Predicate predicate : other) {
multiPredicate = multiPredicate.or(predicate);
}
return multiPredicate;
}
}
Applying to the question:
public static void main(String... args) {
List list = new ArrayList<>();
Map filterMap = new HashMap<>();
list.add(new TestObject(1, 2, 3));
list.add(new TestObject(1, 2, 4));
list.add(new TestObject(1, 4, 3));
filterMap.put(3, 3); // Filter property3 == 3
filterMap.put(2, 2); // Filter property2 == 2
List> filters = filterMap.entrySet().stream()
.map(filterMapEntry -> mapToFilter(filterMapEntry))
.collect(Collectors.toList());
Predicate multiFilter = MultiPredicate.matchingAll(filters);
List filtered = list.stream()
.filter(multiFilter)
.collect(Collectors.toList());
for (TestObject to : filtered) {
System.out.println("(" + to.getProperty(1) + "|" + to.getProperty(2) + "|" + to.getProperty(3) + ")");
}
}
private static Predicate mapToFilter(Entry filterMapEntry) {
return to -> to.getProperty(filterMapEntry.getKey()) == filterMapEntry.getValue();
}
In this case all filters have to match. The result is:
(1|2|3)
If we use MultiPredicate.matchingAny(...)
the result is:
(1|2|3)
(1|2|4)
(1|4|3)