Search code examples
javaspring-bootjava-streamdtoprojection

How can I be able to perform a filter on a nested List within another java list


Disclaimer: I am really new to Java and i am trying to transform this json in springboot using streams to obtain an output as indicated below. But all my trials are failing, what am i doing wrong with this.

[
    {
        "id": 1,
        "year": 2020,
        "description": "Two thousand twenty",
        "brands": [
            {
                "id": 1,
                "name": "Tesla",
                "description": "This is a car provider from USA",
                "countryId": 16,
                "brandNo": "TE001",
                "categoryId": 23
            },
            {
                "id": 2,
                "name": "Toyota",
                "description": "This is a car provider from Japan",
                "countryId": 16,
                "brandNo": "TE003",
                "categoryId": 23
            }
        ]
    },
    {
        "id": 1,
        "year": 2022,
        "description": "Two thousand twenty",
        "brands": [
            {
                "id": 1,
                "name": "Tesla",
                "description": "This is a car provider from USA",
                "countryId": 16,
                "brandNo": "TE001",
                "categoryId": 23
            },
            {
                "id": 2,
                "name": "Toyota",
                "description": "This is a car provider from Japan",
                "countryId": 16,
                "brandNo": "TE003",
                "categoryId": 23
            }
        ]
    }
]

The output i am look to get is based on if the user passes a name for the brand that "Toyota" then the output should be.

[
    {
        "id": 1,
        "year": 2020,
        "description": "Two thousand twenty",
        "brands": [
            {
                "id": 2,
                "name": "Toyota",
                "description": "This is a car provider from Japan",
                "countryId": 16,
                "brandNo": "TE003",
                "categoryId": 23
            }
        ]
    },
    {
        "id": 1,
        "year": 2022,
        "description": "Two thousand twenty",
        "brands": [
            {
                "id": 2,
                "name": "Toyota",
                "description": "This is a car provider from Japan",
                "countryId": 16,
                "brandNo": "TE003",
                "categoryId": 23
            }
        ]
    }
]

My current code snippet I am using looks like the one below. Any help is highly appreciated.

  List<YearBrandDTO> years = yearRepository.findAll().stream()
                    .map(year -> year.getBrands().stream()
                            .filter(brand -> brand.getName().equals("Toyota")))
                    .map(year -> modelMapper.map(year, YearBrandDTO.class))
                    .collect(Collectors.toList());

And my DTO looks like this

@Data
public class YearBrandDTO {
        private Long id;
        private Integer year;
        private String description;
        private Object brands;
}

But the output i get is null values

   [
        {
            "id": null,
            "year": null,
            "description": null,
            "brands": null
        }
    ]

Solution

  • You're trying to map the result of filtering brands directly to the YearBrandDTO. But you should filter the brands first and then create a new YearBrandDTO object.

    Like this:

    List<YearBrandDTO> years = yearRepository.findAll().stream()
        .map(year -> {
            List<Brand> filteredBrands = year.getBrands().stream()
                .filter(brand -> brand.getName().equals("Toyota"))
                .collect(Collectors.toList());
    
            YearBrandDTO yearBrandDTO = modelMapper.map(year, YearBrandDTO.class);
            yearBrandDTO.setBrands(filteredBrands);
            return yearBrandDTO;
        })
        .collect(Collectors.toList());
    

    This way you should be able to get the desired output. Just update your YearBrandDTO class to have a List<BrandDTO> instead of an Object for the brand's field.

    @Data
    public class YearBrandDTO {
        private Long id;
        private Integer year;
        private String description;
        private List<BrandDTO> brands;
    }
    
    

    In your case, I would need to create a BrandDTO class similar to your YearBrandDTO class to map the brand fields.