Search code examples
javajava-streamnested-listsflatmap

Java stream multiple filter problem on nested lists?


I have the following stream that works without any problem:

final List<ProductCategoryDTO> productCategoryList = productCategoryService
                .findAllByMenu(menuUuid)
                .stream()
                .filter(category -> !category.getProductList().isEmpty())
                .collect(Collectors.toList());

I just want to add another filter:

final List<ProductCategoryDTO> productCategoryList = productCategoryService
                .findAllByMenu(menuUuid)
                .stream()
                .filter(category -> !category.getProductList().isEmpty())
                .flatMap(product -> product.getProductList().stream())
                .filter(menu -> !menu.getMenuItemProperties().isDisabled())
                .collect(Collectors.toList());

But it throws required type is List<ProductCategoryDTO> and returned type is List<MenuItemCategoryDTO>. I also tried to use a single filter by adding && condition to the original filter, but cannot perform this.

.filter(category -> !category.getProductList().isEmpty() 
    && category.getProductList().stream()
   .filter(p -> !p.getMenuItemProperties().isDisabled()))

So, what is the correct usage for this situation? There is a product list in productCategoryList and I need to filter based on menuItemProperties().isDisabled() property. Any idea?

Update:


public class ProductCategoryDTO {
    private UUID uuid;
    private UUID menuUuid;
    private List<MenuItemCategoryDTO> productList;
}

Solution

  • You might try it like this using mapMulti (introduced in Java 16)

    • stream the categories
    • for each Product (if any exist)
      • iterate across the properties for that product
      • continue iterating until at least one is enabled
      • then accept that one which adds the product to the stream.
    • then return a list
    List<ProductCategoryDTO> productCategoryList = productCategoryService
            .findAllByMenu(menuUuid)
            .stream()
            .<ProductCategoryDTO>mapMulti((category, consumer)-> {
                   for(Product prod : getProductList()) {
                       if (prod.getMenuItemsProperties().isDisabled()) {
                           // check next product
                           continue;
                       }
                       // at least one enabled one found so accept category
                       consumer.accept(category);
                       break;
                   } 
            }).collect(Collectors.toList());
    

    If you want to ensure that only categories are accepted where all the properites are enabled, the you can try it like this.

    List<ProductCategoryDTO> productCategoryList = productCategoryService
            .findAllByMenu(menuUuid)
            .stream()
            .<ProductCategoryDTO>mapMulti((category, consumer)-> {
                   for(Product prod : getProductList()) {
                       if (prod.getMenuItemsProperties().isDisabled()) {
                           return;
                       }
                   }
                   // all are enabled so accept category
                   consumer.accept(category);    
            }).collect(Collectors.toList());