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.6
}
]
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>>
.
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);
});