How to create a map of maps from a list of maps with Java 8 streaming api

  • A+
Category:Languages

Background

I have a list of maps that looks something like this:

[   {     "name": "A",     "old": 0.25,     "new": 0.3   },   {     "name": "B",     "old": 0.3,     "new": 0.35   },   {     "name": "A",     "old": 0.75,     "new": 0.7   },   {     "name": "B",     "old": 0.7,     "new": 0.60   } ] 

and I want the output to look like this:

{   "A": {     "old": 1,     "new": 1   },   "B": {     "old": 1,     "new": 0.95   } } 

...where the values of old and new are summed for each related entry.

The data type of the list of maps is List<Map<String, Object>>, so the output should be a Map<String, Map<String, Double>>.

What I've Tried

With some diagram drawing, documentation reading, and trial and error, I was able to come up with this:

data.stream()     .collect(         Collectors.groupingBy(entry -> entry.get("name"),             Collectors.summingDouble(entry ->                 Double.parseDouble(entry.get("old").toString())))     ); 

to produce an object of type Map<String, Double>, where the output is

{   "A": 1,   "B": 1 } 

for the summations of the old values. However, I can't quite transform it into a map of maps. Something like this:

data.stream()     .collect(         Collectors.groupingBy(entry -> entry.get("name"),             Collectors.mapping(                 Collectors.groupingBy(entry -> entry.get("old"),                     Collectors.summingDouble(entry ->                         Double.parseDouble(entry.get("old").toString())                     )                 ),                 Collectors.groupingBy(entry -> entry.get("new"),                     Collectors.summingDouble(entry ->                         Double.parseDouble(entry.get("new").toString())                     )                 )             )         )     ); 

doesn't work, because Collectors.mapping() only takes one mapping function and a downstream collector, but I'm not sure how to map two values at once.

Is there another function I need to create mappings of two different values? Any suggestions on better ways of doing this is greatly appreciated as well.

 


You can use streams, but you can also use Map's computeIfAbsent and merge methods:

Map<String, Map<String, Double>> result = new LinkedHashMap<>(); data.forEach(entry -> {     String name = (String) entry.get("name");     Map<String, Double> map = result.computeIfAbsent(name, k -> new HashMap<>());     map.merge("old", (Double) entry.get("old"), Double::sum);     map.merge("new", (Double) entry.get("new"), Double::sum); }); 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: