Grouping of inner Maps with Java Streams

i have following structure:

Map<String,Map<String,Map<String,Integer>>>

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:

Map<String,List<Entry<String,Map<String,Integer>>>>

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:

        initialMap.values().stream()
                            .flatMap(m -> m.entrySet().stream())
                            .collect(Collectors.groupingBy(Map.Entry::getKey));

Summary:

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

3
Leave a Reply

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

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 »

Jason
Guest

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(); }

Jason
Guest

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}}