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().filter().
The Objects are actually JSON, therefore the Map holds the names of its variables as well as the value they have to contain in order to be accepted, but for simplicity reasons and because its not relevant to the question I wrote a simple Testclass for simulating the behaviour:
public class TestObject {
private int property1;
private int property2;
private int property3;
public TestObject(int property1, int property2, int property3) {
this.property1 = property1;
this.property2 = property2;
this.property3 = property3;
}
public int getProperty(int key) {
switch(key) {
case 1: return property1;
case 2: return property2;
default: return property3;
}
}
}
What I have tried so far:
public static void main(String[] args) {
List<TestObject> list = new ArrayList<>();
Map<Integer, Integer> 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
//Does not apply the result
filterMap.forEach((key, value) -> list.stream()
.filter(testObject -> testObject.getProperty(key) == value)
.collect(Collectors.toList())
);
/* Gives error: boolean can not be converted to void
list = list.stream()
.filter(testObject -> filterMap.forEach((key, value) -> testObject.getProperty(key) == value))
.collect(Collectors.toList()
);
*/
//Printing result
list.forEach(obj -> System.out.println(obj.getProperty(1) + " " + obj.getProperty(2) + " " + obj.getProperty(3)));
}
I tried putting forEach of the Map first and the stream of the Collection first, but both solutions did not work as intended. The desired output of this example would be only to print the object with the values property1=1, property2=2 and property3=3.
How can I apply all filters correctly like when you would put them one after another in the code with a fixed amount of filters?
With a known amount of filters:
list.stream().filter(...).filter(...)
Edit:
Sweeper summed my question up very well in his answer, so just for clarification (and probably future readers) here again: I want to keep all Objects that satisfy all filters.
3.1. You can chain filter method on the stream to evaluate each element against multiple conditions. For example, the below code contains two filters: First filter method evaluates whether the fruit name ends with “fruit” suffix and returns the results “jack fruit” and “dragon fruit”
Combining two filter instances creates more objects and hence more delegating code but this can change if you use method references rather than lambda expressions, e.g. replace filter(x -> x. isCool()) by filter(ItemType::isCool) .
Use negate() to write the reverse/negative conditions so that a single predicate may serve true and false – both scenarios. Use and() to combine two predicates for a logical AND operation. Use or() to combine two predicates for a logical OR operation.
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 listlist
by keeping all the elementsx
that:
- all of the key value pairs
y
offilterMap
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.
To apply a variable number of filter steps to a stream (that only become known at runtime), you could use a loop to add filter steps.
Stream<TestObject> stream = list.stream();
for (Predicate<TestObject> predicate: allPredicates) {
stream = stream.filter(predicate);
}
list = stream.collect(Collectors.toList());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With