Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve a value from HashMap with optional argument

I am learning Java 8 streams and I am trying to refactor a method.

Say I have a School class and a school map that stores all school objects by Id. Each school object contains a student map that stores a bunch of students.

In this scenario, the student Ids are unique across the schools.

I have a function that retrieves a student by the id across all schools.

public Student getStudent(Map<String, School> schoolMap, String studentId) {
    return schoolMap.values().stream()
                             .map(School::getStudentIdToStudentMap)
                             .filter(map -> map.containsKey(studentId))
                             .map(map -> map.get(studentId))
                             .findAny().get();
}

Now, I want to change the function to use schoolId as a filter if available.

public Student getStudent(Map<String, School> schoolMap, 
                          String schoolId /* can be null */, 
                          String studentId) 
{
    // TODO: Something that I have tried
    return schoolMap.get(schoolId)
                    .getStudentIdToStudentMap()
                    .get(studentId);
}

Is there a good way I can combine these two functions? If schoolId is null, get the student from all schools. Else just lookup in the particular school and retrieve the student?

like image 316
SyncMaster Avatar asked Aug 24 '18 18:08

SyncMaster


2 Answers

I bet this is what are you looking for:

public Student getStudent(Map<String, School> schoolMap, String schoolId, String studentId)
{
    return Optional.ofNullable(schoolId)             // schoolId might be null
          .map(id -> Stream.of(schoolMap.get(id)))   // get Stream<School> by  else
          .orElse(schoolMap.values().stream())       // ... get Stream of all Schools
          .flatMap(i -> i.getStudentIdToStudentMap() // students from 1/all schools ...
                         .entrySet().stream())       // flat map to Stream<Entry<..,..>>
          .collect(Collectors.toMap(                 // collect all entries bu key/value
              Entry::getKey, Entry::getValue))       // ... Map<String,Student> 
          .getOrDefault(studentId, null);            // get Student by id or else null
}

You have to search in either the only known school or in all the schools. The idea is based on the common characteristics of the searching process. The finding in any school remains the same regardless you iterate one known school or all the ones.

Alternatively get List<Student> from Optional,

public Student getStudent(Map<String, School> schoolMap, String schoolId, String studentId)
{
    return Optional.ofNullable(schoolId)                          // schoolId might be null
           .map(i -> Collections.singletonList(schoolMap.get(i))) // add School into List
           .orElse(new ArrayList<>(schoolMap.values()))           // ... else all schools
           .stream()
           .map(i -> i.getStudentIdToStudentMap()       // get Map of students from 1/all
                      .get(studentId))                  // ... find by studentId
           .filter(Objects::nonNull)                    // get rid of nulls
           .findFirst().orElse(null);                   // get Student by id or else null
}
like image 93
Nikolas Charalambidis Avatar answered Sep 22 '22 19:09

Nikolas Charalambidis


This would be the clearest way IMO:

public Student getStudent(Map<String, School> schoolMap,
                          String schoolId /* can be null */,
                          String studentId){
    if(schoolId == null){
        return getStudent(schoolMap, studentId); // delegate to overload
    } else{
        return schoolMap.get(schoolId)
            .getStudentIdToStudentMap()
            .get(studentId);
    }
}

It is not always needed to do everything with streams. So split the two apart. Either search through all maps with streams, or just select the student from the schoold where you have the id from.

like image 40
Lino Avatar answered Sep 23 '22 19:09

Lino