Skip to content
Advertisement

Java Stream – Collecting Parent-objects into a Set after applying flatMap()

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()
    );
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement