Search code examples
java-8java-11collectors

java 11 collect to map by keeping value unique


I have a specific json value as shown below,

{
 "record_id" : "r01",
 "teacherNstudents": [
                      {
                       "teacher" : {
                                    "name" : "tony",
                                    "tid"  : "T01"
                                   },
                       "student" : {
                                    "name" : "steve",
                                    "sid" : "S01"
                                   }
                      },
                      {
                       "teacher" : {
                                    "name" : "tony",
                                    "tid"  : "T01"
                                   },
                       "student" : {
                                    "name" : "natasha",
                                    "sid" : "S02"
                                   }
                      },
                      {
                       "teacher" : {
                                    "name" : "tony",
                                    "tid"  : "T01"
                                   },
                       "student" : {
                                    "name" : "bruce",
                                    "sid" : "S03"
                                   }
                      },
                      {
                       "teacher" : {
                                    "name" : "tony",
                                    "tid"  : "T01"
                                   },
                       "student" : {
                                    "name" : "victor",
                                    "sid" : "S04"
                                   }
                      },
                      {
                       "teacher" : {
                                    "name" : "henry",
                                    "tid"  : "T02"
                                   },
                       "student" : {
                                    "name" : "jack",
                                    "sid" : "S05"
                                   }
                      },
                      {
                       "teacher" : {
                                    "name" : "henry",
                                    "tid"  : "T02"
                                   },
                       "student" : {
                                    "name" : "robert",
                                    "sid" : "S06"
                                   }
                      }
                     ]
}

I am trying to generate a map like the one below,

[ {"S01", "T01"} , {"S05","T02"} ]

This is by removing all duplicate values and selecting only one teacher and student. The current code I wrote for this is

var firstMap = records.getTeacherNstudents()
                      .stream()
                      .collect(Collectors.toMap(tS -> tS.getTeacher().getTid(),
                                                tS -> tS.getStudent().getSid(),
                                                (a1, a2) -> a1));
return firstMap.entrySet()
               .stream()
               .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

I believe this can be improved, by using Collectors.groupingBy. I am still working on it, but if anyone has any good idea on how to solve this, please share.


Solution

  • Using Java 8 groupingBy

    You can try the below approach in order to have the Map<String,List<String>> or Map<String,Set<String>>(avoid duplicates) where key of map will be the teacher id and value as List or Set of Students corresponding to each teacher.

    I have used groupingBy feature from java 8 and did the grouping based on the tId and before collecting it, I have downstream it to List or Set of student Ids corresponding to each tId.

    Approach A: Map<String,Set< String >> (Uniques)

    data.getTeacherStudentMappingList()
                    .stream()
                    .collect(Collectors.groupingBy(x -> x.getTeacher().getTid(), LinkedHashMap::new,
                            Collectors.mapping(y -> y.getStudent().getSid(),Collectors.toSet())));
    

    Approach B : Map<String,List< String >> (Non-uniques, duplicates)

    data.getTeacherStudentMappingList()
                .stream()
                .collect(Collectors.groupingBy(x -> x.getTeacher().getTid(), LinkedHashMap::new,
                        Collectors.mapping(y -> y.getStudent().getSid(),Collectors.toList())));
    

    Here,

    • data is the converted object from the given json.
    • LinkedHashmap::new is used to preserve the order of student data from the json in the output.
    • collectors.mapping is used to convert the values corresponding to each key into the student ids.
    • Collectors.toList() will collect the list of student ids in the list.
    • Collectors.toSet() will collect the unique student ids in the set.

    Output:

    {T01=[S01, S02, S03, S04], T02=[S05, S06]}