I have the following function which attempts to progressively narrow down an input collection until a single element is found, i.e. filtering is supposed to stop when a single item has been found as applying additional filters may result in no match at all.
public List<MyObject> determinePotentialCandidates(List<MyObject> allCandidates) {
List<MyObject> candidates = allCandidates.stream()
.filter(this::firstCondition)
.toList();
if (candidates.size() > 1) {
candidates = candidates.stream()
.filter(this::secondCondition)
.toList();
if (candidates.size() > 1) {
candidates = candidates.stream()
.filter(this::thirdCondition)
.collect(Collectors.toList());
}
// ... and so on
}
logResult(candidates);
return candidates;
}
As this is becomes harder to read with each additional nesting level, I was wondering if there is a more concise way to write this.
Preferably the method should perform each filtering step at most once (although input size is small and filtering is inexpensive - this could potentially be ok to be performed multiple times for the same input) and contain a single exit point.
CodePudding user response:
You can put all the conditions into a List
and loop over it, applying one filter on each iteration until there is only one element left.
List<Predicate<MyObject>> conditions = List.of(this::firstCondition, this::secondCondition, this::thirdCondition /*...*/ );
for(int i = 0; i < conditions.size() && allCandidates.size() > 1; i )
allCandidates = allCandidates.stream().filter(conditions.get(i)).toList();
return allCandidates;
CodePudding user response:
I think that you can apply the filter operation to the same first stream and it will give you the expected result :
public List<MyObject> determinePotentialCandidates(List<MyObject> allCandidates) {
List<MyObject> candidates = allCandidates.stream()
.filter(this::firstCondition)
.filter(this::secondCondition)
.filter(this::thirdCondition)
.collect(Collectors.toList());
}
// ... and so on
}
logResult(candidates);
return candidates;
}