Search code examples
javalistdata-structuresjava-streamcomparator

Group and Sort nested Objects using Java Streams


I have a list of DTO objects with the nested list field.

The aim is to group them by id field and merge, and then sort the list using Streams API.

class DTO {
    private Long id;
    private List<ItemDTO> items;
}

class ItemDTO {
    private Long priority;
    private Long value;
}

// input
List<DTO> dtoList = List.of(
 DTO(1, List.of(ItemDTO(1, 1), ItemDTO(7, 2))),
 DTO(2, List.of(ItemDTO(1, 1), ItemDTO(2, 2))),
 DTO(1, List.of(ItemDTO(10, 3), ItemDTO(1, 4)))
);

I need to group these nested objects with the same id field and merge all items in descending order by field priority.

The final result for this dtoList will be something like this:

// output 
List<DTO> resultList = [
        DTO(1, List.of(ItemDTO(10,3), ItemDTO(7,2), ItemDTO(1,1), ItemDTO(1,4)),
        DTO(2, List.of(ItemDTO(2,2), ItemDTO(1,1),
    ];

Can we achieve this with Streams API?


Solution

  • I would start by a simple grouping by to get a map Map<Long,List<DTO>> and stream over the entries of that map and map each to a new DTO. You can extract a method / function to get the ItemDTOs sorted:

    import java.util.Comparator;
    import java.util.List;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    ....
    
    
    Function<List<DTO>, List<ItemDTO>> func =
            list -> list.stream()
                    .map(DTO::getItems)
                    .flatMap(List::stream)
                    .sorted(Comparator.comparing(ItemDTO::getPriority,Comparator.reverseOrder()))
                    .collect(Collectors.toList());
    
    List<DTO> result = 
            dtoList.stream()
                   .collect(Collectors.groupingBy(DTO::getId))
                   .entrySet().stream()
                   .map(entry -> new DTO(entry.getKey(), func.apply(entry.getValue())))
                   //.sorted(Comparator.comparingLong(DTO::getId)) if the resulting list need to be sorted by id
                   .collect(Collectors.toList());