Java - 流过滤器以获取日期列表中几个月的最后一天(不是日历最后一天)
Java - Stream Filter to get Last Day of several months in a list of dates (not calendar last day)
查询:
筛选下面的数据以在列表中查找每个月的最后日期。请注意,在这种情况下,
数据中月份的最后日期可能或
可能与日历月的最后日期不匹配。
预期输出显示在第二个列表中。
研究:
- 我认为
TemporalAdjusters.lastDayOfMonth()
在这种情况下无济于事,因为列表中的最后日期可能与日历月的最后日期匹配,也可能不匹配。
- 我在 Stack Overflow 上查了几个问题,还用谷歌搜索了一下,
但我无法找到与我的需要类似的东西。
我希望这个问题很清楚,并为我指明了如何使用流来完成的方向,
因为我不想使用 for
循环。
示例数据:
Date
Model
Start
End
27-11-1995
ABC
241
621
27-11-1995
XYZ
3456
7878
28-11-1995
ABC
242
624
28-11-1995
XYZ
3457
7879
29-11-1995
ABC
243
627
29-11-1995
XYZ
3458
7880
30-11-1995
ABC
244
630
30-11-1995
XYZ
3459
7881
01-12-1995
ABC
245
633
01-12-1995
XYZ
3460
7882
04-12-1995
ABC
246
636
04-12-1995
XYZ
3461
7883
27-12-1995
ABC
247
639
27-12-1995
XYZ
3462
7884
28-12-1995
ABC
248
642
28-12-1995
XYZ
3463
7885
29-12-1995
ABC
249
645
29-12-1995
XYZ
3464
7886
01-01-1996
ABC
250
648
01-01-1996
XYZ
3465
7887
02-01-1996
ABC
251
651
02-01-1996
XYZ
3466
7888
29-01-1996
ABC
252
654
29-01-1996
XYZ
3467
7889
30-01-1996
ABC
253
657
30-01-1996
XYZ
3468
7890
31-01-1996
ABC
254
660
31-01-1996
XYZ
3469
7891
Screenshot
输出要求:
Date
Model
Start
End
30-11-1995
ABC
244
630
30-11-1995
XYZ
3459
7881
29-12-1995
ABC
249
645
29-12-1995
XYZ
3464
7886
31-01-1996
ABC
254
660
31-01-1996
XYZ
3469
7891
Screenshot
我建议创建一个 Map<YearMonth, List<LocalDate>
并解析所有日期并填充地图。之后,您对所有列表进行排序,每个列表中的最后一个(或第一个取决于排序顺序)值将是您想要的值
嗯,groupingBy
和 maxBy
的组合可能就可以了。
我假设 table 的每条记录都是 Event
:
类型
record Event(LocalDate date, String model, int start, int end) { }
要获取 table 内的月份的最后几天,我们可以利用 groupingBy
。为了对其进行分组,我们可以首先创建一个分组类型。下面,我创建了一个 EventGrouping
记录 1,使用 static
方法将 Event
转换为 EventGrouping
。您想要的输出表明您希望按年-月-模型组合进行分组,因此我们只选择了这两个属性:
public record EventGrouping(YearMonth yearMonth, String model) {
public static EventGrouping fromEvent(Event event) {
return new EventGrouping(YearMonth.from(event.date()), event.model());
}
}
然后,我们可以得到我们想要的结果:
events.stream()
.collect(Collectors.groupingBy(
EventGrouping::fromEvent,
Collectors.maxBy(Comparator.comparing(Event::date))
));
这里发生的是所有流元素都由我们的 EventGrouping
分组,然后选择每个事件组的“最大值”。最大值当然是那个月的最近日期。
注意 maxBy
returns 和 Optional
,用于组为空的情况。另请注意,结果 Map
是无序的。
我们可以分别使用 collectingAndThen
和 map factory 来解决这两个问题:
Map<EventGrouping, Event> map = events.stream()
.collect(groupingBy(
EventGrouping::fromEvent,
() -> new TreeMap<>(Comparator.comparing(EventGrouping::yearMonth)
.thenComparing(EventGrouping::model)),
collectingAndThen(maxBy(Comparator.comparing(Event::date)), Optional::get)
));
注意:groupingBy
、collectingAndThen
和 maxBy
都是从 java.util.stream.Collectors
静态导入的。
- 我们添加了一个
Supplier
个 TreeMap
。 TreeMap
是一个 Map
实现,具有给定比较器的 predictable 顺序。这允许我们迭代按年-月-模型排序的结果条目。
collectingAndThen
允许我们将函数应用于给定 Collector
的结果。如前所述,maxBy
returns 和 Optional
,因为如果源流中没有元素,则 maxBy
不适用。然而,在我们的例子中,这永远不会发生。所以我们可以安全地将 Optional
映射到它包含的值。
1 除了编写自定义类型,您还可以使用现有的 class 来保存两个任意值,例如 Map.Entry
、Pair
甚至 List<Object>
.
查询: 筛选下面的数据以在列表中查找每个月的最后日期。请注意,在这种情况下, 数据中月份的最后日期可能或 可能与日历月的最后日期不匹配。 预期输出显示在第二个列表中。
研究:
- 我认为
TemporalAdjusters.lastDayOfMonth()
在这种情况下无济于事,因为列表中的最后日期可能与日历月的最后日期匹配,也可能不匹配。 - 我在 Stack Overflow 上查了几个问题,还用谷歌搜索了一下, 但我无法找到与我的需要类似的东西。
我希望这个问题很清楚,并为我指明了如何使用流来完成的方向,
因为我不想使用 for
循环。
示例数据:
Date | Model | Start | End |
---|---|---|---|
27-11-1995 | ABC | 241 | 621 |
27-11-1995 | XYZ | 3456 | 7878 |
28-11-1995 | ABC | 242 | 624 |
28-11-1995 | XYZ | 3457 | 7879 |
29-11-1995 | ABC | 243 | 627 |
29-11-1995 | XYZ | 3458 | 7880 |
30-11-1995 | ABC | 244 | 630 |
30-11-1995 | XYZ | 3459 | 7881 |
01-12-1995 | ABC | 245 | 633 |
01-12-1995 | XYZ | 3460 | 7882 |
04-12-1995 | ABC | 246 | 636 |
04-12-1995 | XYZ | 3461 | 7883 |
27-12-1995 | ABC | 247 | 639 |
27-12-1995 | XYZ | 3462 | 7884 |
28-12-1995 | ABC | 248 | 642 |
28-12-1995 | XYZ | 3463 | 7885 |
29-12-1995 | ABC | 249 | 645 |
29-12-1995 | XYZ | 3464 | 7886 |
01-01-1996 | ABC | 250 | 648 |
01-01-1996 | XYZ | 3465 | 7887 |
02-01-1996 | ABC | 251 | 651 |
02-01-1996 | XYZ | 3466 | 7888 |
29-01-1996 | ABC | 252 | 654 |
29-01-1996 | XYZ | 3467 | 7889 |
30-01-1996 | ABC | 253 | 657 |
30-01-1996 | XYZ | 3468 | 7890 |
31-01-1996 | ABC | 254 | 660 |
31-01-1996 | XYZ | 3469 | 7891 |
Screenshot
输出要求:
Date | Model | Start | End |
---|---|---|---|
30-11-1995 | ABC | 244 | 630 |
30-11-1995 | XYZ | 3459 | 7881 |
29-12-1995 | ABC | 249 | 645 |
29-12-1995 | XYZ | 3464 | 7886 |
31-01-1996 | ABC | 254 | 660 |
31-01-1996 | XYZ | 3469 | 7891 |
Screenshot
我建议创建一个 Map<YearMonth, List<LocalDate>
并解析所有日期并填充地图。之后,您对所有列表进行排序,每个列表中的最后一个(或第一个取决于排序顺序)值将是您想要的值
嗯,groupingBy
和 maxBy
的组合可能就可以了。
我假设 table 的每条记录都是 Event
:
record Event(LocalDate date, String model, int start, int end) { }
要获取 table 内的月份的最后几天,我们可以利用 groupingBy
。为了对其进行分组,我们可以首先创建一个分组类型。下面,我创建了一个 EventGrouping
记录 1,使用 static
方法将 Event
转换为 EventGrouping
。您想要的输出表明您希望按年-月-模型组合进行分组,因此我们只选择了这两个属性:
public record EventGrouping(YearMonth yearMonth, String model) {
public static EventGrouping fromEvent(Event event) {
return new EventGrouping(YearMonth.from(event.date()), event.model());
}
}
然后,我们可以得到我们想要的结果:
events.stream()
.collect(Collectors.groupingBy(
EventGrouping::fromEvent,
Collectors.maxBy(Comparator.comparing(Event::date))
));
这里发生的是所有流元素都由我们的 EventGrouping
分组,然后选择每个事件组的“最大值”。最大值当然是那个月的最近日期。
注意 maxBy
returns 和 Optional
,用于组为空的情况。另请注意,结果 Map
是无序的。
我们可以分别使用 collectingAndThen
和 map factory 来解决这两个问题:
Map<EventGrouping, Event> map = events.stream()
.collect(groupingBy(
EventGrouping::fromEvent,
() -> new TreeMap<>(Comparator.comparing(EventGrouping::yearMonth)
.thenComparing(EventGrouping::model)),
collectingAndThen(maxBy(Comparator.comparing(Event::date)), Optional::get)
));
注意:groupingBy
、collectingAndThen
和 maxBy
都是从 java.util.stream.Collectors
静态导入的。
- 我们添加了一个
Supplier
个TreeMap
。TreeMap
是一个Map
实现,具有给定比较器的 predictable 顺序。这允许我们迭代按年-月-模型排序的结果条目。 collectingAndThen
允许我们将函数应用于给定Collector
的结果。如前所述,maxBy
returns 和Optional
,因为如果源流中没有元素,则maxBy
不适用。然而,在我们的例子中,这永远不会发生。所以我们可以安全地将Optional
映射到它包含的值。
1 除了编写自定义类型,您还可以使用现有的 class 来保存两个任意值,例如 Map.Entry
、Pair
甚至 List<Object>
.