Is it possible to go back to the parent-object after applying flatmap()
operation, and accumulate parent-objects into a set?
I have an UnifiedOfferEntity
with set of other entity objects as a field:
public static class UnifiedOfferEntity { private Set<ContractDetailsEntity> contractDetails; // getters, etc. }
I would like to filter through fields of the parent-object (UnifiedOfferEntity
) like this:
offersFromDB.stream() .filter(offer -> CollectionUtils.containsAny(preferences.getOwnedSkills(), offer.getSkills()) && CollectionUtils.containsAny(preferences.getOwnedSeniority(), offer.getSeniority()))
And then I would like to examine the nested collection, filter through child-objects (ContractDetailsEntity
):
.flatMap(offer -> offer.getContractDetails().stream() .filter(cd -> cd.getSalaryFrom() >= preferences.getSalaryMin())
And finally, I need to move back to the parent-object and collect its instances into a Set after these filters.
I was trying with this:
List<UnifiedOfferEntity> offersFromDB = // initializing somehow Set<UnifiedOfferEntity> result = offersFromDB.stream() .filter(offer -> CollectionUtils.containsAny(preferences.getOwnedSkills(), offer.getSkills()) && CollectionUtils.containsAny(preferences.getOwnedSeniority(), offer.getSeniority())) .flatMap(offer -> offer.getContractDetails().stream() .filter(cd -> cd.getSalaryFrom() >= preferences.getSalaryMin() && cd.getSalaryTo() <= preferences.getSalaryMax() && tocPreferences.contains(cd.getTypeOfContract()))) .collect(Collectors.toSet())
But it creates a Set
of ContractDetailsEntity
, not UnifiedOfferEntity
. How can I fix this?
Advertisement
Answer
You can perform filtering based on the contents of the nested collection (a set of ContractDetailsEntity
) and then accumulate enclosing objects (ContractDetails
) for which provided Predicate
has been evaluated to true
by using built-in collector filtering()
, which expects a predicate and a downstream collector.
If I understood correctly, you need only instances of UnifiedOfferEntity
which have all the ContractDetailsEntity
that match a particular Predicate. To filter such offers, you can generate a stream of contractDetails and apply allMatch()
with all the conditions you’ve listed (in case if it’s sufficient when only one instance of ContractDetailsEntity
meets the conditions – apply anyMatch()
instead).
That’s how it might look like:
List<UnifiedOfferEntity> offersFromDB = // initializing offersFromDB Set<UnifiedOfferEntity> result = offersFromDB.stream() .filter(offer -> CollectionUtils.containsAny(preferences.getOwnedSkills(), offer.getSkills()) && CollectionUtils.containsAny(preferences.getOwnedSeniority(), offer.getSeniority())) .collect(Collectors.filtering( offer -> offer.getContractDetails().stream().allMatch(cd -> // a Predicate for evalueating ContractDetails goes here cd.getSalaryFrom() >= preferences.getSalaryMin() && cd.getSalaryTo() <= preferences.getSalaryMax() && tocPreferences.contains(cd.getTypeOfContract())), Collectors.toSet() );