java—填充所有状态值的摘要计数

68de4m5k  于 2021-06-30  发布在  Java
关注(0)|答案(3)|浏览(208)

我有一个 Enum 所有状态如下:

enum Status {    
    ACCEPTED("Accepted"),
    REJECTED("Rejected"),
    FAILED("Failed");

    private final String label;

    Status(final String label) {
        this.label = label;
    }
}

Map我从数据库运行的查询结果的模型:

public class Summary {    
    private LocalDate date;
    private Status status;
    private int count;    
}

假设我的数据库返回以下内容,不包括 Rejected 24日和 Failed 第27名:

DATE        | status        | count
20-01-24    | Accepted      | 1     
20-01-24    | Failed        | 40        
20-01-27    | Accepted      | 10        
20-01-27    | Rejected      | 15

我怎么还可以包括 rejected & failed 与上述日期一起的日期?
我称db为:

final List<Summary> statuses = this.dbRepo.getSummaries(someCriteria);

但这份清单不包括 Rejected & Failed 因为db没有返回它们。
通过Java8和流操作,是否有方法检查所有可用的枚举值 Status 而在那里有一个状态缺失 statuses 对于日期,包括计数0?
到目前为止,我一直在尝试从db获取结果,并将这些结果放入一个Map中,如下所示:

public Map<LocalDate, List<Summary>> getSummaries(final SomeCriteria someCriteria) {
    return this.namedParameterJdbcTemplate.query(this.QUERY,
            new MapSqlParameterSource()
                    .addValue("date", someCriteria.getFrom())
            ((rs, rowNum) -> Summary.builder()
                                                    .date(rs.getDate("date").toLocalDate())
                                                    .status(valueOfLabel(rs.getString("status")))
                                                    .count(rs.getInt("count"))
                                                    .build())
    ).stream().collect(groupingBy(Summary::getDate));
}
crcmnpdw

crcmnpdw1#

这是我将分两步做的事情,在一个穷人版本的笛卡尔自连接中(在预先分组的数据上):

//select status, date, sum(count) group by status, date
Map<Status, Map<LocalDate, Integer>> existingCounts = 
    summaries.stream()
    .collect(Collectors.groupingBy(
        Summary::getStatus, 
        Collectors.groupingBy(Summary::getDate, 
                              Collectors.summingInt(Summary::getCount))));

List<Summary> result = Arrays.stream(Status.values())
    .flatMap(
        status -> existingCounts.values()
                   .stream().distinct()
                   .flatMap(
                           map -> map.keySet().stream()
                                    .map(date -> new SimpleEntry<>(status, date))))
    .map(e -> new Summary(e.getValue(), e.getKey(),
        existingCounts.containsKey(e.getKey())
                ? (existingCounts.get(e.getKey()).containsKey(e.getValue())
                        ? existingCounts.get(e.getKey()).get(e.getValue()).intValue()
                        : 0)
                : 0))
    .collect(Collectors.toList());

上面列出:

[date=2020-01-27, status=ACCEPTED, count=10]
[date=2020-01-24, status=ACCEPTED, count=1]
[date=2020-01-27, status=ACCEPTED, count=10]
[date=2020-01-24, status=ACCEPTED, count=1]
[date=2020-01-27, status=REJECTED, count=15]
[date=2020-01-24, status=REJECTED, count=0]
[date=2020-01-27, status=REJECTED, count=15]
[date=2020-01-24, status=REJECTED, count=0]
[date=2020-01-27, status=FAILED, count=0]
[date=2020-01-24, status=FAILED, count=40]
[date=2020-01-27, status=FAILED, count=0]
[date=2020-01-24, status=FAILED, count=40]
umuewwlo

umuewwlo2#

一步一个脚印地做这件事也许是可能的,但我认为它不会那么易读!分几步做,我要说清楚!
第一步:按日期和状态属性分组。

Map<LocalDate, Set<Status>> groupByDateAndStatus = list.stream()
        .collect(Collectors.groupingBy(Summary::getDate,
               Collectors.mapping(Summary::getStatus, Collectors.toSet())));

{2020-01-27=[接受,拒绝],2020-01-24=[失败,接受]}
在第二步中,组摘要包含日期和可用状态。

Map<LocalDate, List<Summary>> map = list.stream()
            .collect(Collectors.toMap(Summary::getDate,
                summary -> new ArrayList<>(Collections.singletonList(summary)), YOURCLASS::merge));

最后一步加上 summary 缺席 status .

EnumSet.allOf(Status.class)
        .forEach(status -> groupByDateAndStatus.entrySet()
        .stream()
        .filter(entry -> !entry.getValue().contains(status))
        .forEach(entry -> map.merge(entry.getKey(), 
 new ArrayList<Summary>(Collections.singletonList(new Summary(entry.getKey(), status, 0))),
                                                                           YOURCLASS::merge)));

合并方法:

private static List<Summary> merge(List<Summary> l1, List<Summary> l2) {
   l1.addAll(l2);
   return l1; 
}

输出:

{
  2020-01-27=[
      Summary{date=2020-01-27, status=ACCEPTED, count=10}, 
      Summary{date=2020-01-27, status=REJECTED, count=15}, 
      Summary{date=2020-01-27, status=FAILED, count=0}], 
  2020-01-24=[ 
      Summary{date=2020-01-24, status=ACCEPTED, count=1}, 
      Summary{date=2020-01-24, status=FAILED, count=40}, 
      Summary{date=2020-01-24, status=REJECTED, count=0}
    ]
}
Map<LocalDate, List<Summary>> map = new HashMap<>();
Map<LocalDate, Map<Status, Summary>> lookup = list.stream()
        .collect(Collectors.groupingBy(Summary::getDate,
            Collectors.toMap(Summary::getStatus, Function.identity())));

EnumSet.allOf(Status.class)
        .forEach(status -> lookup
            .forEach((key, value) ->
               map.merge(key, new ArrayList<>(Collections.singletonList(value.getOrDefault(status, new Summary(key, status, 0)))),
                                    YOURCLASS::merge)));
eoxn13cs

eoxn13cs3#

可以使用嵌套 grouping 创建初始查找 Map ```
Map<LocalDate, Map<Status, Summary>> lookup = statuses.stream()
.collect(Collectors.groupingBy(Summary::getDate,
Collectors.toMap(Summary::getStatus, Function.identity())));

然后在迭代所有 `Status` 每个日期的值,例如:

List overallResult = lookup.entrySet().stream()
.flatMap(en -> EnumSet.allOf(Status.class).stream()
.map(status -> en.getValue().containsKey(status) ?
en.getValue().get(status) : Summary.builder()
.date(en.getKey()).status(status).count(0).build()))
.collect(Collectors.toList());

根据问题中提供的信息,这将导致:

Summary(date=2020-01-27, status=ACCEPTED, count=10)
Summary(date=2020-01-27, status=REJECTED, count=15)
Summary(date=2020-01-27, status=FAILED, count=0)
Summary(date=2020-01-24, status=ACCEPTED, count=1)
Summary(date=2020-01-24, status=REJECTED, count=0)
Summary(date=2020-01-24, status=FAILED, count=40)

相关问题