java collectors with grouping by

157 Views Asked by At

I have a list of MyClass bellow

public class MyClass {
  private String level;
  private String grade;
  private String value;
  
  // constructor, getters, setters
}
List<MyClass> myList = new ArrayList<>();
myList.add(new MyClass("Lv1", "A", "value"));
myList.add(new MyClass("Lv2", "B", "value"));
myList.add(new MyClass("Lv2", "B", "value"));
myList.add(new MyClass("Lv3", "C", "value"));
myList.add(new MyClass("Lv3", "D", "value"));

and there's a grade list

List<String> gradeList = new ArrayList<>();
gradeList.add("A");
gradeList.add("B");
gradeList.add("C");
gradeList.add("D");
gradeList.add("E");

I want to get the count based on gradeList

as Map<String, Map<String, Long>> format.

I don't know how to apply gradeList to myList stream.

// I guess

        myList.stream().collect(Collectors.groupingBy(
            MyClass::getLevel,
            Collectors.groupingBy(
                // do something with gradeList
                , Collectors.counting())));
// expected result : 
{
  "Lv1"={"A"=1,"B"=0,"C"=0,"D"=0,"E"=0, }, 
  "Lv2"={"A"=0,"B"=2,"C"=0,"D"=0,"E"=0, }, 
  "Lv3"={"A"=0,"B"=0,"C"=1,"D"=1,"E"=0, }, 
}

May I get your opinion?

1

There are 1 best solutions below

0
On BEST ANSWER

You can chain two groupingBy() collectors, but the output will not contain grades with 0 count:

Map<String,Map<String,Long>> grouped =
    myList.stream().collect(Collectors.groupingBy(
        MyClass::getLevel,
        Collectors.groupingBy(
            MyClass::getGrade,
            Collectors.counting())));

You can add the grades having 0 counts with a second statement:

grouped.forEach ((k,v)-> {gradeList.forEach (grade -> v.putIfAbsent (grade, 0L));});

The grouped Map created by the first statement:

{Lv1={A=1}, Lv3={C=1, D=1}, Lv2={B=2}}

The grouped Map after the second statement:

{Lv1={A=1, B=0, C=0, D=0, E=0}, Lv3={A=0, B=0, C=1, D=1, E=0}, Lv2={A=0, B=2, C=0, D=0, E=0}}