Search code examples
javahashmapjava-streamcollectors

How to create a nested map Map<String, Map<String,String>> with Stream API


How can I write the logic to obtain a Map<String, Map<String, String>> using streams?

Here is the code I have tried :

public Map<String, Map<String, String>> getData(Set<String> Ids) {
    List<Events> events = repository.findAllByIdIn(Ids);

    int i = 0;
    return events.stream()
            .sorted(Comparator.comparing(Events::getMajor).thenComparing(Events::getMinor))
            .collect(Collectors.groupingBy(Events::getId, 
    //Error-Cannot resolve method 'getMajor()'
    Collectors.mapping(cae -> cae.getMajor() + "." + cae.getMinor(), 
        cae.getDates().get(i).getEndDate() != null ? "Predicted" : "Not Predicted"), 
             Collectors.toMap())));//Error-Cannot resolve method 'toMap()'
}

So how do I loop through a map inside another map?


Solution

  • Collector toMap() contrary to collectors toList() and toSet() isn't parameterless, you need to provide information how a key and a value should be extracted from a single stream element.

    Collectors.toMap() expects at least two functions: keyMapper and valueMapper. In case if there could be duplicates, you need to use the flavor of toMap which takes a mergeFunction as a third argument to resolve the values that collide on the same key.

    So you need to place toMap() as a second argument (downstream collector) into the groupingBy. That will be a syntactically correct structure of the collectors and functions:

    Collectors.groupingBy(classifier, Collectors.toMap(keyMapper, valueMapper))
    

    And that how the code might look like:

    return events.stream()
            .sorted(Comparator.comparing(Events::getMajor)
                .thenComparing(Events::getMinor))
            .collect(Collectors.groupingBy(Events::getId,
                Collectors.toMap(cae -> cae.getMajor() + "." + cae.getMinor(),
                    cae -> cae.getDates().get(i).getEndDate() != null ? 
                        "Predicted" : "Not Predicted")));
    

    Note, Collectors.mapping() isn't meant to produce a Map but to transform the elements of the stream, you can think of it as an analog of the map() operation that is done not is the middle of the stream pipeline, but during the process of collecting the data.

    And if I understood your logic correctly, there's no need to apply mapping() here.