Group By in java 8 with multiple levels of grouping

Viewed 318

I have such structure

| Director1 | Manager1 | Employee1 |
| Director1 | Manager1 | Employee2 |
| Director1 | Manager2 | Employee3 |
| Director2 | Manager3 | Employee4 |

I fetch this data from Db to Pojo like this

@Data
public class Workers {
 private String directorName;
 private String managerName;
 private String employeeName;
}

DB is denormalized.

Mow in java code I need to group this response to not to return redundant data. My response data looks like

@Data
public class WorkersResponse{
 private String directorName;
 ...
 private List<Manager> managers;
}

@Data
public class Manager{
 private String manager;
  ...
 private List<Employee> employee;
}

@Data
public class Employee{
 private String employee;
 ...
}

With grouping I want to receive List. For now I figured out how to group directors :

Map<WorkersResponse, List<Workers >> collect = all.stream()
            .collect(Collectors.groupingBy(v -> {
                WorkersResponse workersResponse= new WorkersResponse();
                workersResponse.setDirector(v.getDirectorName());
                return workersResponse;
            }));

But how should then I populate list with Managers and Employees?

2 Answers

Based on the grouping requirement, you should try using nested grouping along with Collectors.mapping such as:

Map<String, Map<String, List<String>>> groupingRequirement = workersList.stream()
        .collect(Collectors.groupingBy(Workers::getDirectorName,
                Collectors.groupingBy(Workers::getManagerName,
                        Collectors.mapping(Workers::getEmployeeName,
                                Collectors.toList()))));

Thereafter mapping to objects of the desired type is the only constraint left while you iterated over the entries of the collected Map -

List<WorkersResponse> workersResponses = groupingRequirement.entrySet().stream()
        .map(e -> new WorkersResponse(e.getKey(), // director name
                e.getValue().entrySet()
                        .stream()
                        .map(ie -> new Manager(ie.getKey(), // manager name
                                ie.getValue()
                                        .stream()
                                        .map(Employee::new)
                                        .collect(Collectors.toList())))
                        .collect(Collectors.toList())))
        .collect(Collectors.toList());

@Naman already given the better answer, just want to add, you can get WorkersResponse in this way also using collectingAndThen

List<WorkersResponse> requirement = workersList.stream()
        .collect(Collectors.collectingAndThen(Collectors.groupingBy(Workers::getDirectorName,
                Collectors.collectingAndThen(Collectors.groupingBy(Workers::getManagerName,
                        Collectors.mapping(w -> new Employee(w.getEmployeeName()),Collectors.toList())),
                    e -> e.entrySet().stream().map(ie -> new Manager(ie.getKey(), ie.getValue()))
                          .collect(Collectors.toList()))),
            e -> e.entrySet().stream().map(ie -> new WorkersResponse(ie.getKey(), ie.getValue()))
                          .collect(Collectors.toList())));
Related