背景
我有一个地图列表,如下所示:
[
{
"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
}
]
我希望输出是这样的:
{
"A": {
"old": 1,
"new": 1
},
"B": {
"old": 1,
"new": 0.95
}
}
对于每个相关条目,将对old
和new
的值求和。
映射列表的数据类型是List<Map<String, Object>>
,因此输出应该是Map<String, Map<String, Double>>
。
我尝试过的
通过一些图表绘制、文档阅读和反复试验,我能够得出以下结论:
data.stream()
.collect(
Collectors.groupingBy(entry -> entry.get("name"),
Collectors.summingDouble(entry ->
Double.parseDouble(entry.get("old").toString())))
);
生成Map<String, Double>
类型的对象,其中输出为
{
"A": 1,
"B": 1
}
获取old
值的总和。但是,我不能完全将其转换为地图。如下所示:
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())
)
)
)
)
);
不起作用,因为Collectors.mapping()
只接受一个映射函数和一个下游收集器,但我不确定如何同时映射两个值。
是否有其他函数需要创建两个不同值的映射?任何关于更好的方法的建议也是非常感谢的。
发布于 2018-10-05 01:29:33
您可以使用streams,但也可以使用Map
的computeIfAbsent
和merge
方法:
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);
});
发布于 2018-10-05 01:18:05
您的第一次尝试已接近解决方案,但是您需要执行一些自定义代码才能完全完成图片。
您将需要实现您自己的collector,一个将多个map转换为一个double map。
这看起来像这样:
Collector.of(
() -> new HashMap<>(),
(Map<String, Double>target, Map<String, Object> source) -> {
target.merge("old", (Double)source.get("old"), Double::sum);
target.merge("new", (Double)source.get("new"), Double::sum);
},
(Map<String, Double> map1, Map<String, Double> map2) -> {
map2.forEach((k, v) -> map1.merge(k, v, Double::sum));
return map1;
}
)
这与您的初始尝试分组相结合,解决了以下问题:
data.stream()
.collect(
Collectors.groupingBy(entry -> entry.get("name"),
// Insert collector here
)
);
在线完整代码示例:http://tpcg.io/pJftrJ
发布于 2018-10-05 01:00:18
您可以声明一个名为Pair
的类
public class Pair {
private final double oldVal;
private final double newVal;
public Pair(double oldVal, double newVal) {
super();
this.oldVal = oldVal;
this.newVal = newVal;
}
public double getOldVal() {
return oldVal;
}
public double getNewVal() {
return newVal;
}
@Override
public String toString() {
return "{oldVal=" + oldVal + ", newVal=" + newVal + "}";
}
}
然后像这样做,
Map<Object, Pair> result = sourceMaps.stream()
.collect(Collectors.toMap(m -> m.get("name"),
m -> new Pair((double) m.get("old"), (double) m.get("new")),
(p1, p2) -> new Pair(p1.getOldVal() + p2.getOldVal(), p1.getNewVal() + p2.getNewVal())));
这是输出结果,
{A={oldVal=1.0, newVal=1.0}, B={oldVal=1.0, newVal=0.95}}
https://stackoverflow.com/questions/52651635
复制相似问题