java - 如何使用 Java 8 收集器对三重嵌套地图求和
问题描述
我有这张地图 Map<LocalDate, Map<Integer, Map<EHourQuarter, Double>>>
EHourQuarter 是一个枚举:
public enum EHourQuarter {
FIRST(0, 14, 15),
SECOND(15, 29, 30),
THIRD(30, 44, 45),
FOURTH(45, 59, 60);
private Integer start;
private Integer end;
private Integer value;//this is for UI purposes
}
使用如下值:{2020-07-07 -> {0 -> {EHourQuarter.FIRST -> 5.5, EHourQuarter.SECOND -> 10.2, ...}, 1 -> {EHourQuarter.FIRST -> 33.2, EHourQuarter.SECOND -> 30.1, ...}, ...},
2020-07-08 -> {0 -> {EHourQuarter.FIRST -> 5.5, EHourQuarter.SECOND -> 10.2, ...}, 1 -> {EHourQuarter.FIRST -> 33.2, EHourQuarter.SECOND -> 30.1, . ..}, ... }
它是 Double 的 EHourQuarter 地图的 Integer 地图的 LocalDate 地图(小时:从 0 到 23)。
我需要得到一个包含每个日期累积的 Map<Integer(hour), Map<EHourQuarter, Double>>,这意味着如果日期 2020-07-07 到 2020-07-10(4 天)包含在小时 0每个 EHourQuarter 每一个 5,那么结果应该显示在 0 小时,每个季度的值为 20。
此外,如果通过这样做,您还可以帮助我将其映射到像这样的 DTO 列表,
public class QuarterlyOccupancyDTO {
private Integer hour;
private Integer minute;//this is the value property of EHourQuarter
private Double occupancy;
}
我会很感激的。
最后,DTO 列表应包含按小时和分钟分组的所有日期的总和(EHourQuarter 的 value 属性)。
这是一个例子。
注意:一张地图可以包含多个日期,目的是对所有的进行分组/求和。
鉴于这张地图:
{
"2020-06-26":{
"0":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"1":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"2":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"3":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"4":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"5":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"6":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"7":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"8":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"9":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"10":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"11":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"12":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"13":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"14":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"15":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"16":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"17":{
"FOURTH":5.0,
"FIRST":5.0,
"THIRD":5.0,
"SECOND":5.0
},
"18":{
"FOURTH":0.0,
"FIRST":5.0,
"THIRD":0.0,
"SECOND":0.0
},
"19":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"20":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"21":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"22":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
},
"23":{
"FOURTH":0.0,
"FIRST":0.0,
"THIRD":0.0,
"SECOND":0.0
}
}
}
像这样的列表应该是答案:
[
{
"hour":0,
"minute":15,
"occupancy":0.0
},
{
"hour":0,
"minute":30,
"occupancy":0.0
},
{
"hour":0,
"minute":45,
"occupancy":0.0
},
{
"hour":0,
"minute":60,
"occupancy":0.0
},
{
"hour":1,
"minute":15,
"occupancy":0.0
},
{
"hour":1,
"minute":30,
"occupancy":0.0
},
{
"hour":1,
"minute":45,
"occupancy":0.0
},
{
"hour":1,
"minute":60,
"occupancy":0.0
},
{
"hour":2,
"minute":15,
"occupancy":0.0
},
{
"hour":2,
"minute":30,
"occupancy":0.0
},
{
"hour":2,
"minute":45,
"occupancy":0.0
},
{
"hour":2,
"minute":60,
"occupancy":0.0
},
{
"hour":3,
"minute":15,
"occupancy":0.0
},
{
"hour":3,
"minute":30,
"occupancy":0.0
},
{
"hour":3,
"minute":45,
"occupancy":0.0
},
{
"hour":3,
"minute":60,
"occupancy":0.0
},
{
"hour":4,
"minute":15,
"occupancy":0.0
},
{
"hour":4,
"minute":30,
"occupancy":0.0
},
{
"hour":4,
"minute":45,
"occupancy":0.0
},
{
"hour":4,
"minute":60,
"occupancy":0.0
},
{
"hour":5,
"minute":15,
"occupancy":0.0
},
{
"hour":5,
"minute":30,
"occupancy":0.0
},
{
"hour":5,
"minute":45,
"occupancy":0.0
},
{
"hour":5,
"minute":60,
"occupancy":0.0
},
{
"hour":6,
"minute":15,
"occupancy":0.0
},
{
"hour":6,
"minute":30,
"occupancy":0.0
},
{
"hour":6,
"minute":45,
"occupancy":0.0
},
{
"hour":6,
"minute":60,
"occupancy":0.0
},
{
"hour":7,
"minute":15,
"occupancy":0.0
},
{
"hour":7,
"minute":30,
"occupancy":0.0
},
{
"hour":7,
"minute":45,
"occupancy":0.0
},
{
"hour":7,
"minute":60,
"occupancy":0.0
},
{
"hour":8,
"minute":15,
"occupancy":0.0
},
{
"hour":8,
"minute":30,
"occupancy":0.0
},
{
"hour":8,
"minute":45,
"occupancy":0.0
},
{
"hour":8,
"minute":60,
"occupancy":0.0
},
{
"hour":9,
"minute":15,
"occupancy":5.0
},
{
"hour":9,
"minute":30,
"occupancy":5.0
},
{
"hour":9,
"minute":45,
"occupancy":5.0
},
{
"hour":9,
"minute":60,
"occupancy":5.0
},
{
"hour":10,
"minute":15,
"occupancy":5.0
},
{
"hour":10,
"minute":30,
"occupancy":5.0
},
{
"hour":10,
"minute":45,
"occupancy":5.0
},
{
"hour":10,
"minute":60,
"occupancy":5.0
},
{
"hour":11,
"minute":15,
"occupancy":5.0
},
{
"hour":11,
"minute":30,
"occupancy":5.0
},
{
"hour":11,
"minute":45,
"occupancy":5.0
},
{
"hour":11,
"minute":60,
"occupancy":5.0
},
{
"hour":12,
"minute":15,
"occupancy":5.0
},
{
"hour":12,
"minute":30,
"occupancy":5.0
},
{
"hour":12,
"minute":45,
"occupancy":5.0
},
{
"hour":12,
"minute":60,
"occupancy":5.0
},
{
"hour":13,
"minute":15,
"occupancy":5.0
},
{
"hour":13,
"minute":30,
"occupancy":5.0
},
{
"hour":13,
"minute":45,
"occupancy":5.0
},
{
"hour":13,
"minute":60,
"occupancy":5.0
},
{
"hour":14,
"minute":15,
"occupancy":5.0
},
{
"hour":14,
"minute":30,
"occupancy":5.0
},
{
"hour":14,
"minute":45,
"occupancy":5.0
},
{
"hour":14,
"minute":60,
"occupancy":5.0
},
{
"hour":15,
"minute":15,
"occupancy":5.0
},
{
"hour":15,
"minute":30,
"occupancy":5.0
},
{
"hour":15,
"minute":45,
"occupancy":5.0
},
{
"hour":15,
"minute":60,
"occupancy":5.0
},
{
"hour":16,
"minute":15,
"occupancy":5.0
},
{
"hour":16,
"minute":30,
"occupancy":5.0
},
{
"hour":16,
"minute":45,
"occupancy":5.0
},
{
"hour":16,
"minute":60,
"occupancy":5.0
},
{
"hour":17,
"minute":15,
"occupancy":5.0
},
{
"hour":17,
"minute":30,
"occupancy":5.0
},
{
"hour":17,
"minute":45,
"occupancy":5.0
},
{
"hour":17,
"minute":60,
"occupancy":5.0
},
{
"hour":18,
"minute":15,
"occupancy":5.0
},
{
"hour":18,
"minute":30,
"occupancy":0.0
},
{
"hour":18,
"minute":45,
"occupancy":0.0
},
{
"hour":18,
"minute":60,
"occupancy":0.0
},
{
"hour":19,
"minute":15,
"occupancy":0.0
},
{
"hour":19,
"minute":30,
"occupancy":0.0
},
{
"hour":19,
"minute":45,
"occupancy":0.0
},
{
"hour":19,
"minute":60,
"occupancy":0.0
},
{
"hour":20,
"minute":15,
"occupancy":0.0
},
{
"hour":20,
"minute":30,
"occupancy":0.0
},
{
"hour":20,
"minute":45,
"occupancy":0.0
},
{
"hour":20,
"minute":60,
"occupancy":0.0
},
{
"hour":21,
"minute":15,
"occupancy":0.0
},
{
"hour":21,
"minute":30,
"occupancy":0.0
},
{
"hour":21,
"minute":45,
"occupancy":0.0
},
{
"hour":21,
"minute":60,
"occupancy":0.0
},
{
"hour":22,
"minute":15,
"occupancy":0.0
},
{
"hour":22,
"minute":30,
"occupancy":0.0
},
{
"hour":22,
"minute":45,
"occupancy":0.0
},
{
"hour":22,
"minute":60,
"occupancy":0.0
},
{
"hour":23,
"minute":15,
"occupancy":0.0
},
{
"hour":23,
"minute":30,
"occupancy":0.0
},
{
"hour":23,
"minute":45,
"occupancy":0.0
},
{
"hour":23,
"minute":60,
"occupancy":0.0
}
]
解决方案
第一组并按小时/季度对合计占用率
(避免嵌套flatMap
,因为它降低了代码的可读性)
Map<Entry<Integer, Integer>, Double> groups
= map.entrySet()
.stream()
// Flatten the outer map, since you don't care about the days
.flatMap(de -> de.getValue().entrySet().stream())
// Flatten the map by combining hour key and quarter key into a single one
.flatMap(he -> he.getValue()
.entrySet()
.stream()
.map(qe -> new SimpleEntry<>(new SimpleEntry<>(he.getKey(), qe.getKey().getValue()), qe.getValue())))
// Sum the occupancy per each hour/quarter pair
.collect(groupingBy(Entry::getKey, summingDouble(Entry::getValue)));
然后将分组的条目映射到您的 DTO 对象中
List<QuarterlyOccupancyDTO> list =
groups.entrySet()
.stream()
.map(e -> new QuarterlyOccupancyDTO(e.getKey().getKey(), e.getKey().getValue(), e.getValue()))
.collect(toList());
另一种纯函数方法:
(它是纯粹的,但似乎不太可读,IMO)
Collection<QuarterlyOccupancyDTO> dtos =
map.entrySet()
.stream()
// Flatten the outer map, since you don't care about the days
.flatMap(de -> de.getValue().entrySet()
.stream())
// Flatten the map by merging hour key and quarter key into a single one
.flatMap(he -> he.getValue()
.entrySet()
.stream()
.map(qe -> new SimpleEntry<>(new SimpleEntry<>(he.getKey(), qe.getKey().getValue()),
qe.getValue())))
// Map each entry into a DTO object and then reduce the occupancy per each hour/quarter pair
.collect(
groupingBy(Entry::getKey,
mapping(e -> new QuarterlyOccupancyDTO(e.getKey().getKey(), e.getKey().getValue(), e.getValue()),
reducing(new QuarterlyOccupancyDTO(0, 0, 0.0),
(a, b) -> new QuarterlyOccupancyDTO(b.getHour(), b.getMinute(), a.getOccupancy() + b.getOccupancy())))))
.values();
推荐阅读
- c# - 如何检查是否在 KeyUp 事件的文本框中输入了数字或字母
- flutter - 设备锁定时颤动动画侦听器不起作用
- eclipse - 限制 Eclipse RCP 项目中打开的选项卡
- r - 根据日期/时间范围和匹配 id 过滤数据
- java - 如何解决读取 JSON 文件以获取名字和姓氏的失败执行?
- oracle - 使用 Cursor 的 SQL 问题 - 进行第一次交易的案例研究
- javascript - 是否可以限制本地开发服务器的上传速度?
- c++ - 在构造函数错误中将类指针作为参数传递
- python - 如何在使用 python 启动时让 selenium 登录到网站
- c++ - 我可以使 operator== virtual 来实现相等吗?