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

homersimpson :

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.

Federico Peralta Schaffner :

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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=35796&siteId=1