当前位置: 首页 > 知识库问答 >
问题:

将LocalDates映射的内容分组为日期范围映射

戚云
2023-03-14

我有一张按天索引的工作班次图map

"2020-03-26": [
    {
      "id": 4,
      "startTime": "21:00:00"
    },
    {
      "id": 5,
      "startTime": "09:00:00",
    }
  ],
  "2020-03-27": [
    {
      "id": 4,
      "startTime": "22:00:00"
    },
    {
      "id": 5,
      "startTime": "10:00:00",
    }
  ],
  ...
]

可以看到,班次有一个id和一个开始时间。

但将地图按天索引是一种浪费:变化碰巧是一致的,除了一件事。开始时间是LocalTime,因此班次的唯一区别是,在应用时区后,当DST翻转发生时,产生的班次仅在小时内不同。

我想在收集器中执行某种groupBy,以获得类似的功能:

Range.of(2020-01-01 ... 2020-03-26) [
    {
      "id": 4,
      "startTime": "21:00:00"
    },
    {
      "id": 5,
      "startTime": "09:00:00",
    }
],
Range.of(2020-03-27 ... 2020-10-30) [
    {
      "id": 4,
      "startTime": "22:00:00"
    },
    {
      "id": 5,
      "startTime": "10:00:00",
    }
],
Range.of(2020-10-31 ... 2021-01-01) [
    {
      "id": 4,
      "startTime": "21:00:00"
    },
    {
      "id": 5,
      "startTime": "09:00:00",
    }
]

...分组在从DST翻转到DST翻转的范围内。我很难写一个收集器。groupBy(…)并将密钥从单个LocalDateday转换为范围


共有1个答案

闻慎之
2023-03-14

首先,您需要定义一个具有两个字段(开始日期和结束日期)的类Range,并创建一个范围列表。由于只需要三个实例,因此将此列表声明为实用程序类中的公共静态最终字段是有意义的。

下面是范围的一个例子,为了简洁起见,我将其实现为Java 16 record。给出了一种实用方法,用于检查给定日期是否在此范围内:

public record Range(LocalDate start, LocalDate end) {
    public boolean isWithinRange(LocalDate date) { // start inclusive, end exclusive
        return date.isBefore(start) || date.isBefore(end) && date.isAfter(start);
    }
}

范围列表:

public static final List<Range> RANGES =
    List.of(new Range(LocalDate.of(2020,1, 1), LocalDate.of(2020,3, 27)),
        new Range(LocalDate.of(2020,3, 27), LocalDate.of(2020,10, 31)),
        new Range(LocalDate.of(2020,10, 31), LocalDate.of(2021,1, 1)));

实用方法getRange()负责根据给定日期从范围列表中检索Range的实例:

public static Range getRange(LocalDate data) {
    return RANGES.stream()
        .filter(range -> range.isWithinRange(data))
        .findFirst()
        .orElseThrow();
}

虚拟记录Shift(用于测试目的):

public record Shift(int id, LocalTime startTime) {}

为了接近地图的转换

main()-demo

public static void main(String[] args) {
    Map<LocalDate, Collection<Shift>> shiftsByDate =
        Map.of(LocalDate.of(2020,3, 26),
                   List.of(new Shift(4, LocalTime.of(21, 0, 0)), new Shift(5, LocalTime.of(9, 0, 0))),
               LocalDate.of(2020,3, 27),
                   List.of(new Shift(4, LocalTime.of(22, 0, 0)), new Shift(5, LocalTime.of(10, 0, 0)))
            );
    
    Map<Range, List<Shift>> shiftsByRange = shiftsByDate.entrySet().stream()
        .collect(Collectors.groupingBy(entry -> getRange(entry.getKey()),
            Collectors.flatMapping(entry -> entry.getValue().stream(),
                Collectors.toList())));
    
    shiftsByRange.forEach((k, v) -> System.out.println(k + " : " + v));
}

输出

Range[start=2020-10-31, end=2021-01-01] : [Shift[id=4, startTime=22:00], Shift[id=5, startTime=10:00]]
Range[start=2020-01-01, end=2020-03-27] : [Shift[id=4, startTime=21:00], Shift[id=5, startTime=09:00]]

 类似资料:
  • 感谢所有人!

  • 问题内容: 抱歉,是否曾有人问过这个问题,但我进行了广泛调查,没有结果。 我想创建一个新列,该列根据某些规则映射的多个值,例如a = [1,2,3]为1,a = [4,5,6,7]为2,a = [8 ,9,10]为3。一对一映射对我来说很清楚,但是如果我想按值列表或范围进行映射怎么办? 我遵循这些原则… 问题答案: 有几种选择。 熊猫通过/ NumPy通过 NumPy通过 的元素是布尔级数,因此对

  • 问题内容: 我正在寻找一种确定范围的方法 。 Golang规范指出以下内容: 未指定地图的迭代顺序,并且不能保证每次迭代之间都相同。如果在迭代过程中删除尚未到达的映射条目,则不会生成相应的迭代值。如果映射条目是在迭代过程中创建的,则该条目可能在迭代过程中产生或可以被跳过。对于创建的每个条目以及从一个迭代到下一个迭代,选择可能有所不同。如果映射为nil,则迭代次数为0。 我在StackOverflo

  • 在web应用部署描述符中,以下语法用于定义映射: 以‘/’字符开始、以‘/*’后缀结尾的字符串用于路径匹配。 以‘*.’开始的字符串用于扩展名映射。 空字符串“”是一个特殊的URL模式,其精确映射到应用的上下文根,即,http://host:port//请求形式。在这种情况下,路径信息是‘/’且servlet路径和上下文路径是空字符串(“”)。 只包含“/”字符的字符串表示应用的“default”

  • 我试图收集一个列表的结果,并将它们组织成一个地图,其中的值是一个地图: 我得到错误,因为对于列表中的不同值是相同的。 映射中包含的值应为: 当我尝试会删除其中一个条目

  • 问题内容: 我有一个表,例如此数据 我想有一个查询,将日期分开,所以我有像这样一年中分开的金额: 2013年有25个月是因为2013年有1个月,而2014年有75个月是因为有3个月 有没有办法在T-SQL中做到这一点? 提前谢谢! 问题答案: 使用表格创建日历表格,然后将其加入表格以将日期范围划分为所需的任何部分。 如果按年份除以金额,然后除以月份,您可以: 这是SQL FIDDLE DEMO。