Martin M J
Martin M J

Reputation: 880

Filtering collection stream inside collection stream before returning

Background information

I've got the following classes:

Insurance

public class Insurance {
    ...
}

Customer

public class Customer {
    private List<Insurance> insurances;

    public List<Insurance> getInsurances() {
        return insurances;
    }
    ...
}

CustomerRegistry

public class CustomerRegistry {
    private List<Customer> customers;
    ...
}

as well as this helper method, which reduces a List<Predicate<T>> into a single Predicate<T>:

public Predicate<T> reducePredicates(List<Predicate<T>> predicates) {
    return predicates.stream()
                     .reduce(Predicate::and)
                     .orElse(p -> true);
}

The problem

What I want to do is get a list of insurances that match a list of filters, belonging to customers that match a list of filters. If this is unclear, the below code will hopefully clarify.

Method is inside CustomerRegistry class above.

public List<Insurance> findInsurances(List<Predicate<Customer>> cusPredicates,
List<Predicate<Insurance>> insPredicates) {

    List<Insurance> matches = new LinkedList<>();
    customers.stream()
             .filter(reducePredicates(cusPredicates)
             .forEach(cus -> cus.getInsurances()
                               .stream()
                               .filter(reducePredicates(insPredicates))
                               .forEach(cus -> matches.add(cus)))
    return matches;
}

Is there a way to do this without the matches list? Can I perform some sort of reduction, so that the matching insurances are returned directly (i.e. not being added to a temporary collection like matches)?

Upvotes: 20

Views: 26635

Answers (1)

JB Nizet
JB Nizet

Reputation: 692231

Use flatMap():

customers.stream()
         .filter(reducePredicates(cusPredicates))
         .flatMap(cus -> cus.getInsurances().stream())
         .filter(reducePredicates(insPredicates))
         .collect(Collectors.toList())

Or better, to avoid reducing the predicates over and over again:

Predicate<Customer> customerPredicate = reducePredicates(cusPredicates);
Predicate<Customer> insurancePredicate = reducePredicates(insPredicates);
List<Insurance> = 
    customers.stream()
             .filter(customerPredicate)
             .flatMap(cus -> cus.getInsurances().stream())
             .filter(insurancePredicate)
             .collect(Collectors.toList())

Upvotes: 20

Related Questions