'java stream list of filters
Let's say I've got a list of Strings and I want to filter them by the list of filtering Strings. For the list which consists of: "abcd", "xcfg", "dfbf"
I would precise list of filtering Strings: "a", "b", and after something like filter(i->i.contains(filterStrings) I would like to receive list of "abcd", "dfbf", And for a list of filtering Strings: "c", "f" I would like to reveive list of "xcfg" and "dfbf".
List<String> filteredStrings = filteredStrings.stream()
.filter(i -> i.contains("c") || i.contains("f")) //i want to pass a list of filters here
.collect(Collectors.toList());
Is there an other way of doing this instead of expanding body of lambda expression and writing a function with a flag which will check every filter?
Solution 1:[1]
You should instead be performing a anyMatch over the list to match from:
List<String> input = Arrays.asList("abcd", "xcfg", "dfbf"); // your input list
Set<String> match = new HashSet<>(Arrays.asList("c", "f")); // to match from
List<String> filteredStrings = input.stream()
.filter(o -> match.stream().anyMatch(o::contains))
.collect(Collectors.toList());
Solution 2:[2]
You can create a List, that has an arbitrary number of filters
List<Predicate<String>> filterList = new ArrayList<>();
Predicate<String> containsCPredicate = s -> s.contains("c");
filterList.add(containsCPredicate);
filterList.add(s -> s.contains("f"));
...
You can then reduce the list of these filters into one, combining them:
filterList.stream().reduce(i -> true, Predicate::and)
when applying them on your stream pipeline, like
List<String> filteredStrings = strings.stream()
.filter(filterList.stream().reduce(i -> true, Predicate::and))
.collect(Collectors.toList());
The initial i -> true Predicate is the identity, the reduce operation needs as the first parameter.
Solution 3:[3]
You can change your contains with a simple regex :
.filter(i -> i.matches(".*[cf].*")) // to check just one character
or :
.filter(i -> i.matches(".*(c|f).*")) // or if you have a words
Solution 4:[4]
A filter can be represented as a Predicate. In your case a Predicate<String>. So, a list of filter can be stored in a List<Predicate<String>>.
Now, if you want to apply such a list on each element of your Stream:
List<String> filteredStrings = input.stream()
.filter(i -> filterList.stream().anyMatch(f -> f.test(i)))
.collect(Collectors.toList());
To complete the example:
List<String> input = new ArrayList<>(Arrays.asList ("abcd", "xcfg", "dfbf","erk"));
List<Predicate<String>> filterList = new ArrayList<>();
filterList.add (i -> i.contains("c"));
filterList.add (i -> i.contains("f"));
List<String> filteredStrings = input.stream()
.filter(i -> filterList.stream().anyMatch(f -> f.test(i)))
.collect(Collectors.toList());
System.out.println (filteredStrings);
Output:
[abcd, xcfg, dfbf]
Solution 5:[5]
You can use a Pattern:
static final Pattern pattern = Pattern.compile("c|f");
And then check if a string matches said pattern.
List<String> strings = filteredStrings.stream()
.filter(s -> pattern.matcher(s).find())
.collect(Collectors.toList());
That pattern can of course be computed by a given input:
public static Pattern compute(String... words) {
StringBuilder pattern = new StringBuilder();
for(int i = words.length - 1; i >= 0; i++) {
pattern.append(words[i]);
if(i != 0) {
pattern.append('|');
}
}
return Pattern.compile(pattern);
}
Which could then be called like this:
Pattern patten = compute("some", "words", "hello", "world");
Which would result in a regex:
some|words|hello|world
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | guest |
| Solution 4 | |
| Solution 5 | Lino |
