Grouping of inner Maps with Java Streams

i have following structure:


Now i want to disregard the First-level-Maps and group (and sum up) the 3rd-Level-Maps according to the key of the 2nd-Level-Maps.
To Clarify some example-Entries:

Entry 1: ["1"["A"[[a,1];[b,2]];"B"[[a,3];[c,1]]]]
Entry 2: ["2"["A"[[b,2];[c,1]];"B"[[a,5];[b,0]]]]

Desired output:

Entry 1: ["A"[[a,1];[b,4];[c,1]]]
Entry 4: ["B"[[a,8];[b,0];[c,1]]]

So to do this I first group my Entry-stream according to my 2nd-Level-Keys (“A”,”B”) and, if nothing else done, end up with a structure like the following:


And here is where I am stuck. How do i go about getting my Map<String,Integer>from my List of Entries (for each outer Map, secifically)?

The simple code which I assume is guaranteed to be needed:

                            .flatMap(m -> m.entrySet().stream())


How do I transform a Map<String,Map<String,Map<String,Integer>>> to a Map<String<Map<String,Integer>>, disregarding the outermost Map, grouping my innermost Maps according to my 2nd-Layer-Keys and summing my Integer-values by key-values of the Innermost Map.
Additionally the outermost Maps each have a Key-Value-Pair for each 2nd-Level-Map, so each will have the same 2nd-Level-Keys. In the 3rd-Level-Keysets can be Keys not found in other 3rd-Level-Maps

Leave a Reply

3 Comment threads
0 Thread replies
Most reacted comment
Hottest comment thread
1 Comment authors
Jason Recent comment authors
newest oldest most voted
Notify of

A thing to keep in mind here: streams conceptually represent a single element coming down through a “pipe” of sorts. It’s always single element when the stream runs, no matter if source has one, multiple or infinite number of elements backed up in total. What you’re trying to do here is represent several nested loops, along the lines of: Map<String, Map<String, Integer>> result = new HashMap<>(); for (Map<String, Map<String, Integer>> firstMap : inputMap.values()) { for (Entry<String, Map<String, Integer>> firstEntry : firstMap.entrySet()) { String upperCaseKey = firstEntry.getKey(); Map<String, Ingeter> resultEntry = result.computeIfAbsent( upperCaseKey, _k -> new HashMap<>()); for (Entry<String, Integer> secondEntry… Read more »


Map<String, Map<String, Integer>> result = initialMap .values() .stream() .flatMap(m -> m.entrySet().stream()) .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.groupingBy(e -> mapToFirstEntry(e.getValue()).getKey(), Collectors.summingInt(e -> mapToFirstEntry(e.getValue()).getValue())))); but it assumes that a third-level Map<String, Integer> contains one entry and there is a method to get that entry: public static Map.Entry<String, Integer> mapToFirstEntry(Map<String, Integer> map) { return map.entrySet().iterator().next(); }


If you have the liberty of using Java9, I would suggest you to use the flatMapping collector to solve this problem. This approach is much more readable and generates less visual clutter to me. Here’s how it looks. Map<String, Map<String, Integer>> summaryMap = map.values().stream() .flatMap(m -> m.entrySet().stream()) .collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.flatMapping(e -> e.getValue().entrySet().stream(), Collectors.groupingBy(Map.Entry::getKey, Collectors.summingInt(Map.Entry::getValue))))); This program produces the following output: {A={a=1, b=4, c=1}, B={a=8, b=0, c=1}}