Reputation: 333
Say I have mappings from Strings to a Mapping from Strings to int, such as
Map<String, Map<String, Integer>> myMap1 = new HashMap<>();
myMap1.put("A", Map.of("X", 1))
myMap1.put("B", Map.of("Y", 1))
Map<String, Map<String, Integer>> myMap2 = new HashMap<>();
myMap2.put("B", Map.of("Y", 3))
I would like to merge these mappings such that we get a mapping where the key is the inner map's key, and the value would be the average of the inner maps values of the same keys. So the output to the example above would be
{"X" : 1, "Y", 2}
We can discard the outer map's key altogether.
What is the nicest way to do this with java. I thought there might be some nice way to do it with Collectors.groupBy method but I am quite inexperienced with this.
Upvotes: 0
Views: 1967
Reputation: 44292
I’m going to assume there might be more than two maps, so let’s make a List out of them:
Collection<Map<String, Map<String, Integer>>> myMaps =
List.of(myMap1, myMap2);
Then we can use flatMap on the values()
of each Map, which gives us a stream of Map<String, Integer>
maps.
We can obtain the entrySet()
of each of those, then apply flatMap to the streams of those entry sets, to give us a single Stream of Map.Entry<String, Integer>
objects, which we can then group.
There is a groupingBy method which takes a second Collector for customizing the values of the groups, by collecting all of the grouped values seen. We can use that to get our averages, using an averaging collector.
Map<String, Double> averages =
myMaps.stream().flatMap(map -> map.values().stream()) // stream of Map<String, Integer>
.flatMap(innerMap -> innerMap.entrySet().stream()) // stream of Map.Entry<String, Integer>
.collect(Collectors.groupingBy(Map.Entry::getKey, // group by String key
Collectors.averagingInt(Map.Entry::getValue))); // value for each key = average of its Integers
Upvotes: 2