按范围日期分组
GroupingBy a range dates
我遇到这样的情况:
List<ObjectA> collapsed = list.stream().collect(Collectors.collectingAndThen(
Collectors.groupingBy(
ObjectA::getDate,
Collectors.maxBy(Comparator
.comparing(ObjectA::getPriority1)
.thenComparing(ObjectA::getPriority2)
.thenComparing(ObjectA::getDate)
.thenComparing(ObjectA::getId))),
map -> map.values().stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())));
并且它在相同的日期上完美运行。
我只需要将某个日期的项目分组 window:
Integer daysWindow = 90;
所以我希望在 window 中的日期发生所有事情。
这怎么可能?
感谢大家!
您可以在开始时应用过滤器,如下所示,保持其余部分不变。
LocalDate today = LocalDate.now();
list.stream().filter(obj -> !obj.getDate().isAfter(today.plusDays(90)))
.collect(Collectors.collectingAndThen(....
注意: 我假设 ObjectA::getDate
的类型是 java.time.LocalDate
。
演示:
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(inRange(LocalDate.now()));
System.out.println(inRange(LocalDate.of(2021, 12, 31)));
System.out.println(inRange(LocalDate.of(2022, 2, 20)));
}
static boolean inRange(LocalDate date) {
return !date.isAfter(LocalDate.now().plusDays(90));
}
}
输出:
true
false
false
了解有关 modern Date-Time API* from Trail: Date Time 的更多信息。
* 无论出于何种原因,如果您必须坚持Java 6 或Java 7,您可以使用ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and 。
作为另一种方法,如果您要查找的是日期范围到相应值的映射,则可以通过创建范围数据类型,然后在该类型上生成组来完成更多工作。这是一个例子:
class DateRange {
private final LocalDate from;
private final int length;
public DateRange(LocalDate from, int length) {
super();
this.from = from;
this.length = length;
}
//getters
@Override
public int hashCode() {
return this.from.hashCode(); //TODO
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DateRange))
return false;
DateRange r = (DateRange) obj;
return this.length == r.length && this.from.equals(r.from);
}
@Override
public String toString() {
return "(" + from + " to " + from.plusDays(length) + ")";
}
}
然后您的流可以创建一个映射,就像您当前拥有的映射一样,但在确定每个元素所属的范围后,按日期范围分组。
int daysWindow = 60;
var start = LocalDate.of(2021, 1, 1);
//example date list
List<LocalDate> dates = List.of(start.plusDays(20),
start.plusDays(30), start.plusDays(40), start.plusDays(50),
start.plusDays(60), start.plusDays(70), start.plusDays(80));
var ranges = dates.stream()
.collect(Collectors.groupingBy(d -> {
var diff = ChronoUnit.DAYS.between(start, d);
return new DateRange(start.plusDays(diff - diff % daysWindow), daysWindow);
}));
当然,您将使用当前收集器,我只是想展示对 groupingBy
的调用,您仍然可以使用下游收集器调用它。
您需要确定的一件事是 start
的值。我假设这可以设置为集合中的最小日期,尽管它也可以是用户定义的。
以上代码输出结果如下:
{(2021-03-02 to 2021-05-01)=[2021-03-02, 2021-03-12, 2021-03-22],
(2021-01-01 to 2021-03-02)=[2021-01-21, 2021-01-31, 2021-02-10, 2021-02-20]}
并且当使用不同的 window、int daysWindow = 90;
进行测试时:
{(2021-01-01 to 2021-04-01)=[2021-01-21, 2021-01-31, 2021-02-10,
2021-02-20, 2021-03-02, 2021-03-12, 2021-03-22]}
我遇到这样的情况:
List<ObjectA> collapsed = list.stream().collect(Collectors.collectingAndThen(
Collectors.groupingBy(
ObjectA::getDate,
Collectors.maxBy(Comparator
.comparing(ObjectA::getPriority1)
.thenComparing(ObjectA::getPriority2)
.thenComparing(ObjectA::getDate)
.thenComparing(ObjectA::getId))),
map -> map.values().stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())));
并且它在相同的日期上完美运行。 我只需要将某个日期的项目分组 window:
Integer daysWindow = 90;
所以我希望在 window 中的日期发生所有事情。 这怎么可能?
感谢大家!
您可以在开始时应用过滤器,如下所示,保持其余部分不变。
LocalDate today = LocalDate.now();
list.stream().filter(obj -> !obj.getDate().isAfter(today.plusDays(90)))
.collect(Collectors.collectingAndThen(....
注意: 我假设 ObjectA::getDate
的类型是 java.time.LocalDate
。
演示:
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(inRange(LocalDate.now()));
System.out.println(inRange(LocalDate.of(2021, 12, 31)));
System.out.println(inRange(LocalDate.of(2022, 2, 20)));
}
static boolean inRange(LocalDate date) {
return !date.isAfter(LocalDate.now().plusDays(90));
}
}
输出:
true
false
false
了解有关 modern Date-Time API* from Trail: Date Time 的更多信息。
* 无论出于何种原因,如果您必须坚持Java 6 或Java 7,您可以使用ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and
作为另一种方法,如果您要查找的是日期范围到相应值的映射,则可以通过创建范围数据类型,然后在该类型上生成组来完成更多工作。这是一个例子:
class DateRange {
private final LocalDate from;
private final int length;
public DateRange(LocalDate from, int length) {
super();
this.from = from;
this.length = length;
}
//getters
@Override
public int hashCode() {
return this.from.hashCode(); //TODO
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DateRange))
return false;
DateRange r = (DateRange) obj;
return this.length == r.length && this.from.equals(r.from);
}
@Override
public String toString() {
return "(" + from + " to " + from.plusDays(length) + ")";
}
}
然后您的流可以创建一个映射,就像您当前拥有的映射一样,但在确定每个元素所属的范围后,按日期范围分组。
int daysWindow = 60;
var start = LocalDate.of(2021, 1, 1);
//example date list
List<LocalDate> dates = List.of(start.plusDays(20),
start.plusDays(30), start.plusDays(40), start.plusDays(50),
start.plusDays(60), start.plusDays(70), start.plusDays(80));
var ranges = dates.stream()
.collect(Collectors.groupingBy(d -> {
var diff = ChronoUnit.DAYS.between(start, d);
return new DateRange(start.plusDays(diff - diff % daysWindow), daysWindow);
}));
当然,您将使用当前收集器,我只是想展示对 groupingBy
的调用,您仍然可以使用下游收集器调用它。
您需要确定的一件事是 start
的值。我假设这可以设置为集合中的最小日期,尽管它也可以是用户定义的。
以上代码输出结果如下:
{(2021-03-02 to 2021-05-01)=[2021-03-02, 2021-03-12, 2021-03-22],
(2021-01-01 to 2021-03-02)=[2021-01-21, 2021-01-31, 2021-02-10, 2021-02-20]}
并且当使用不同的 window、int daysWindow = 90;
进行测试时:
{(2021-01-01 to 2021-04-01)=[2021-01-21, 2021-01-31, 2021-02-10,
2021-02-20, 2021-03-02, 2021-03-12, 2021-03-22]}