简体   繁体   中英

How to filter based on list returned by map param using Java 8 streams

I'm trying to use Java stream to filter some values based on certain conditions. I am able to achieve the same using traditional for loops and a little bit of streams, but I want to rewrite the same logic fully in streams.

Original code:

public List <String> getProductNames(Hub hub, String requestedGroup) {

    List <SupportedProduct> configuredProducts = repo.getSupportedProducts(hub);

    List <String> productNames = new ArrayList <> ();

    for (SupportedProduct supportedProduct: configuredProducts) {
        List < String > categoryNameList = new ArrayList <> ();
        String activeCategoryName = supportedProduct.getCategoryDetails().getActiveCategoryName();
        if (activeCategoryName == null) {
            Optional.ofNullable(supportedProduct.getCategoryDetails().getCategories())
                .orElse(Collections.emptyList())
                .forEach(category - > categoryNameList.add(category.getName()));
        } else {
            categoryNameList.add(activeCategoryName);
        }
        for (String catName: categoryNameList) {
            Division division = divisionRepo.getDivisionByCatName(catName);
            if (division != null && division.getGroup() == requestedGroup) {
                productNames.add(supportedProduct.getProductName());
            }
        }
    }

    return productNames;

}

My try:

return Optional.ofNullable(configuredProducts).orElse(Collections.emptyList()).stream()
                .map(supportedProduct -> {
                    List<String> categoryNameList = new ArrayList<>();

                    String activeCategoryName = supportedProduct.getCategoryDetails().getActiveCategoryName();

                    if (activeCategoryName == null) {
                        Optional.ofNullable(supportedProduct.getCategoryDetails().getCategories())
                                .orElse(Collections.emptyList())
                                .forEach(category -> categoryNameList.add(category.getName()));
                    } else {
                        categoryNameList.add(activeCategoryName);
                    }
                    return categoryNameList;
                })
                .filter(catName ->{
                    Division division = divisionRepo.getDivisionByCatName(catName);
                    return division != null && division.getGroup() == requestedGroup;
                })........

But I'm lost beyond this.

Please help me to write the same using streams.

EDIT: Added IDEOne for testing - Link

The logic inside is quite complicated, however, try this out:

public List <String> getProductNames(Hub hub, String requestedGroup) {
    List<SupportedProduct> configuredProducts = repo.getSupportedProducts(hub);

    // extract pairs: 
    //    key=SupportedProduct::getProductName
    //    values=List with one activeCategoryName OR names of all the categories
    Map<String, List<String>> namedActiveCategoryNamesMap = configuredProducts.stream()
        .collect(Collectors.toMap(
                SupportedProduct::getProductName,
                p -> Optional.ofNullable(p.getCategoryDetails().getActiveCategoryName())
                        .map(Collections::singletonList)
                        .orElse(Optional.ofNullable(p.getCategoryDetails().getCategories())
                                        .stream()
                                        .flatMap(Collection::stream)
                                        .map(Category::getName)
                                        .collect(Collectors.toList()))));

    // look-up based on the categories' names, group equality comparison and returning a List
    return namedActiveCategoryNamesMap.entrySet().stream()
        .filter(entry -> entry.getValue().stream()
                .map(catName -> divisionRepo.getDivisionByCatName(catName))
                .filter(Objects::nonNull)
                .map(Division::getGroup)
                .anyMatch(requestedGroup::equals))
        .map(Map.Entry::getKey)
        .collect(Collectors.toList());
}
  • I recommend splitting into separate methods for sake of readability ( the best way to go ).
  • The verbose logics of Optional chains including two orElse calls can be surely simplified, however, it gives you the idea.

  • You can perform within one Stream using Collectors.collectingAndThen . In that case, I'd extract the Function finisher elsewhere, example:
public List<String> getProductNames(Hub hub, String requestedGroup) {
    return repo.getSupportedProducts(hub).stream()
            .collect(Collectors.collectingAndThen(
                    Collectors.toMap(
                            SupportedProduct::getProductName,
                            categoryNamesFunction()),
                    productNamesFunction(requestedGroup)));
}
private Function<Map<String, List<String>>, List<String>> productNamesFunction(String requestedGroup) {
    return map -> map.entrySet().stream()
            .filter(entry -> entry.getValue().stream()
                    .map(divisionRepo::getDivisionByCatName)
                    .filter(Objects::nonNull)
                    .map(Division::getGroup)
                    .anyMatch(requestedGroup::equals))
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
}
private Function<SupportedProduct, List<String>> categoryNamesFunction() {
    return p -> Optional.ofNullable(p.getCategoryDetails().getActiveCategoryName())
            .map(Collections::singletonList)
            .orElse(Optional.ofNullable(p.getCategoryDetails().getCategories())
                    .stream()
                    .flatMap(Collection::stream)
                    .map(Category::getName)
                    .collect(Collectors.toList()));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM