Java Collectors groupBy with special behavior for certain key

69 Views Asked by At

I have a collection of objects:

List<PdfTitleCustomizationDto> dtoList = Arrays.asList(
            new PdfTitleCustomizationDto("Title 1", 1, "RRF", Set.of(new Role("ADMIN"))),
            new PdfTitleCustomizationDto("Title 2", 2, "Other", Set.of(new Role("USER"))),
            new PdfTitleCustomizationDto("Title 3", 3, "RRF", Set.of(new Role("ADMIN"))),
            new PdfTitleCustomizationDto("Title 4", 4, "Other", Set.of(new Role("ADMIN")))
    );

I need to group it by channel, which is "RRF" or "Other. However, if channel is "RRF", I have to get only one title with min order. So the expected result should be:

{RRF=[Title 1], Other=[Title 4, Title 2]}

Do you have any ideas how to do it in one stream?

2

There are 2 best solutions below

4
Sweeper On BEST ANSWER

You can groupBy, and in the downstream, use teeing to split the stream into 2 - one does minBy, the other does toList. In the merger argument, you would check the title and decide whether to use the list, or the minimum element.

var y = list.stream().collect(
        Collectors.groupingBy(
                PdfTitleCustomizationDto::getChannel,
                Collectors.teeing(
                        Collectors.minBy(Comparator.comparing(PdfTitleCustomizationDto::getTitle)),
                        Collectors.toList(),
                        (min, list) -> min
                                .filter(x -> "RRF".equals(x.getTitle()))
                                .map(List::of)
                                .orElse(list)
                )
        )
);

This does mean that everything, including those without the "RRF" title, will be minBy'ed, which is some extra processing that you wouldn't need to do otherwise.

If that is a problem, I recommend just groupBy, and remove the non-min elements from the resulting map, or just don't use streams at all.

1
Allan Romanato On

I believe you can use Collector to get what you need

Have a look at the following code.

Map<String,List<PdfTitleCustomizationDto>> result = dtoList.stream()
    .collect(Collectors.groupingBy(
        PdfTitleCustomizationDto::getChannel,
        Collectors.collectingAndThen(
            Collectors.minBy(Comparator.comparingInt(PdfTitleCustomizationDto::getOrder)),
            optionalDto - > optionalDto.map(Collections::singletonList).orElse(Collections.emptyList())
        )
    ));