【问题标题】:Getting Weekly Max value rom获取每周最大值 rom
【发布时间】:2021-12-15 02:13:16
【问题描述】:

问题:

  • 我有一个 Map<LocalDate, Integer>,我想使用 LocalDate 按周对这些地图元素进行分组,并使用 Java 8 Stream 按周查找每个分组值的最大值。

  • 我目前遇到无法将密钥转换回流中的LocalDate 对象的问题,因为我没有必要的数据字段来将LocalDate 的对象创建到地图中。

  • 如果我不将其转换为 LocalDate 并将其保留为 String,我将无法使用 TreeMap 使用键按自然顺序排列键值。

  • 如果我在对数据进行分组时未将月份包含在 DateTimeFormatter.ofPattern 中,则两年之间的周数可能会重叠。

  • 如果我只使用周数整数作为流中地图的输出,则按周分组的值将与另一年的周数混合并重叠,即 2020-2021 和第 1 周。

问题:

  • 如何使用 java 流获得每周值的最大值?

数据集:

Map<LocalDate,Integer> CumulativeGarbageWasteDate = new HashMap<LocalDate,Integer>();
    CumulativeGarbageWasteDate.put(LocalDate.parse(("5/2/2020"),DateTimeFormatter.ofPattern("[M/d/yyyy][M/d/yy]")),2400);
    CumulativeGarbageWasteDate.put(LocalDate.parse(("12/24/20"),DateTimeFormatter.ofPattern("M/d/yy")),140);
    CumulativeGarbageWasteDate.put(LocalDate.parse(("5/2/20"),DateTimeFormatter.ofPattern("M/d/yy")),2400);
    
    CumulativeGarbageWasteDate.put(LocalDate.parse(("01/1/21"),DateTimeFormatter.ofPattern("M/d/yy")),182);
    CumulativeGarbageWasteDate.put(LocalDate.parse(("01/2/21"),DateTimeFormatter.ofPattern("M/d/yy")),203);
    CumulativeGarbageWasteDate.put(LocalDate.parse(("01/3/21"),DateTimeFormatter.ofPattern("M/d/yy")),321);
    CumulativeGarbageWasteDate.put(LocalDate.parse(("01/3/21"),DateTimeFormatter.ofPattern("M/d/yy")),421);
    CumulativeGarbageWasteDate.put(LocalDate.parse(("5/2/2021"),DateTimeFormatter.ofPattern("[M/d/yyyy][M/d/yy]")),2400);
    CumulativeGarbageWasteDate.put(LocalDate.parse(("01/6/21"),DateTimeFormatter.ofPattern("M/d/yy")),1200);
    CumulativeGarbageWasteDate.put(LocalDate.parse(("01/31/21"),DateTimeFormatter.ofPattern("M/d/yy")),2400);

代码:

private static Map<LocalDate, Integer> sumByWeek (Map<LocalDate,Integer> CumulativeGarbageWasteDate){
    
    DateTimeFormatter formatter3 = new DateTimeFormatterBuilder().appendPattern("w/M/YY").parseDefaulting(ChronoField.DAY_OF_WEEK,DayOfWeek.FRIDAY.getValue()).toFormatter();
    

    return CumulativeGarbageWasteDate
            .entrySet()
            .stream()
            .collect(Collectors.groupingBy(
                    row-> LocalDate.parse(row.getKey().format(DateTimeFormatter.ofPattern("w/M/YY")),formatter3),
                    TreeMap::new,
                    Collectors.reducing(0, x->x.getValue(),Math::max)));
}

错误:

这是我在运行代码时遇到的错误。

java.time.format.DateTimeParseException: Text '5/2/20' could not be parsed: Conflict found: Field MonthOfYear 1 differs from MonthOfYear 2 derived from 2020-01-31

【问题讨论】:

  • 欢迎来到 Stack Overflow。我强烈建议您使用 tour 了解 Stack Overflow 的工作原理并阅读 How to Ask。这将帮助您提高问题的质量。对于每个问题,请显示您尝试过的尝试以及您从尝试中得到的错误消息。
  • @McPringle 嗨,我已经更新了错误内容以及之前添加的尝试,并更新了标题以便更好地澄清。感谢您的反馈。
  • 如果Hashmap&lt;K,V&gt;不支持重复键导致两个日期相同怎么办
  • 您是否考虑过 this 在给定 LocalDate 对象的情况下获取一年中一周的 int 值的方法?
  • @ArvindKumarAvinash 你知道它还没有完全解决吗?为什么我要发布一个我没有得到正确答案的问题的答案?

标签: java java-8 java-stream


【解决方案1】:

输出映射的类型为Map&lt;LocalDate, Integer&gt;,其中键是该周具有最高值的日期。 (我正在拆分解决方案以使其更易于理解)。

首先,我建议创建一个类(可能是内部类)来保存日期及其对应的值。这将使处理变得容易。

private static class MyData {
    private LocalDate date;
    private int value;
    
    // getters and a constructor
}

注意:假设在下面的代码中静态导入Collectors 方法,如groupingtoMaptoListmapping


可以使用WeekFields.(Refer) 检索一年中的哪一周

注意: 不应在以下代码中使用 row.getKey().getYear() 而不是 row.getKey().get(weekFields.weekBasedYear()) 来获取年份,因为它可能会在以下情况下产生不正确的结果: 2022-01-012021-12-31,属于 2022 年的第 1 周。 所以如果我们使用getYear(),那么分别生成的字符串 将是1/20221/2021,这是不正确的。 在这种情况下,预期的字符串是1/2022

WeekFields weekFields = WeekFields.of(Locale.getDefault());

// function to generate a key of the form: week~year. year included to avoid overlap
Function<Entry<LocalDate, Integer>, String> groupFn 
    = row -> String.format("%d/%d",
                           row.getKey().get(weekFields.weekOfWeekBasedYear()),
                           row.getKey().get(weekFields.weekBasedYear()));

现在将输入转换为MyData 对象:

Map<String, List<MyData>> temp = 
    input.entrySet()
         .stream()
         .collect(groupingBy(groupFn,
                             TreeMap::new, 
                             mapping(e -> new MyData(e.getKey(), e.getValue()), toList())));

现在我们在一周内找到最大值的日期:

Function<Entry<String, List<MyData>>, MyData> maxDataFn 
    = entry -> entry.getValue()
                    .stream()
                    .collect(collectingAndThen(maxBy(Comparator.comparingInt(MyData::getValue)),
                                               Optional::get));

TreeMap<LocalDate, Integer> result 
    = temp.entrySet()
          .stream()
          .map(maxDataFn)
          .collect(toMap(MyData::getDate, MyData::getValue, Integer::max, TreeMap::new));

【讨论】:

  • 你能分享一下你是如何转换成w/M/yy的吗?
  • @dancisdotedd 您能否提及将其显示为 w/M/yy 的用例。是作为 API 的一部分返回吗? LocalDate 对象包含有关一周的所有必要信息。因此,您可以使用通常的格式化程序本身将其转换为所需的格式。
  • 啊,我打算让它的 localdate 输出只具体地 w/M/yy,但这是不可能的,因为没有日期可以解析为 localdate。但是将字符串输出格式化为 w/M/yy 有效。
  • 您也可以创建一个代表YearWeek的类(或记录),而不是将其“串起来”。
  • 重要:不要使用row.getKey().getYear(),而应使用row.getKey().get(weekFields.weekBasedYear()),否则当某年某个日期的周数属于另一个时,年/年周数组合很奇怪年。例如,2022-01-01,属于 2021 年的第 52 周,使用您当前的代码,这将产生 2022-W52,而它应该是 2021-W52
猜你喜欢
  • 2021-12-04
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 2022-11-10
  • 2021-07-19
  • 2021-07-20
  • 2021-09-15
  • 1970-01-01
相关资源
最近更新 更多