根据日期范围按键过滤多图
Filter Multimap By Key Based on Date Range
我有一个包含由日期和金额组成的付款交易的数据集。我将这些存储在地图数据结构中,日期作为键,金额作为值。
由于每个日期可能有多次付款,我使用 Google Guava 库中的 Multimap。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
Multimap<LocalDate,BigDecimal> payments = ArrayListMultimap.create();
payments.put(LocalDate.parse("12/25/2016",formatter), new BigDecimal("1000"));
payments.put(LocalDate.parse("01/15/2017",formatter), new BigDecimal("250"));
payments.put(LocalDate.parse("01/25/2017",formatter), new BigDecimal("500"));
payments.put(LocalDate.parse("03/20/2017",formatter), new BigDecimal("500"));
payments.put(LocalDate.parse("04/15/2017",formatter), new BigDecimal("1000"));
payments.put(LocalDate.parse("06/15/2017",formatter), new BigDecimal("1000"));
根据日期范围过滤此地图的推荐方法是什么?
例如,显示3/2/2017-3/31/2017之间的条目。
您需要对 Multimap
进行排序,否则您必须全部迭代。还好有这么一个multimap:
ListMultimap<LocalDate, BigDecimal> multimap =
MultimapBuilder.treeKeys().arrayListValues().build();
... fill
// This cast is safe.
SortedMap<LocalDate, Collection<BigDecimal>> asMap =
(SortedMap<LocalDate, Collection<BigDecimal>>) multimap.asMap();
SortedMap<LocalDate, Collection<BigDecimal>> subMap =
asMap.subMap(from, to);
for (Map.Entry<LocalDate, Collection<BigDecimal>> e : subMap.entrySet()) {
...
}
您编码的Multimaps#filterKeys
method allows you to filter an existing Multimap
by keys matching any arbitrary Predicate
。它的 return 值是一个 Multimap
,只包含满足过滤谓词的条目。
首先,让我们定义一个辅助方法,它可以创建一个 Predicate
来检查日期是否在指定范围内。
private static Predicate<LocalDate> between(final LocalDate begin, final LocalDate end) {
return new Predicate<LocalDate>() {
@Override
public boolean apply(LocalDate date) {
return (date.compareTo(begin) >= 0 && date.compareTo(end) <= 0);
}
};
}
之后,您可以使用谓词来过滤您想要的范围。
void testFilterPayments() {
Multimap<LocalDate, BigDecimal> payments = ArrayListMultimap.create();
payments.put(LocalDate.parse("2016-12-25"), new BigDecimal("1000"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("250"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("1250"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("2250"));
payments.put(LocalDate.parse("2017-01-25"), new BigDecimal("500"));
payments.put(LocalDate.parse("2017-03-20"), new BigDecimal("500"));
payments.put(LocalDate.parse("2017-04-15"), new BigDecimal("1000"));
payments.put(LocalDate.parse("2017-06-15"), new BigDecimal("1000"));
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2017-01-01"), LocalDate.parse("2017-04-01"))));
// Output:
// {2017-01-25=[500], 2017-03-20=[500], 2017-01-15=[250, 1250, 2250]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2017-01-01"), LocalDate.parse("2017-01-15"))));
// Output:
// {2017-01-15=[250, 1250, 2250]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2016-01-01"), LocalDate.parse("2017-12-31"))));
// Output:
// {2017-06-15=[1000], 2017-01-25=[500], 2017-03-20=[500], 2016-12-25=[1000], 2017-01-15=[250, 1250, 2250], 2017-04-15=[1000]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2001-01-01"), LocalDate.parse("2015-12-31"))));
// Output:
// {}
}
我已简化此示例以使用默认日期格式进行解析。您的原始示例似乎使用了自定义 formatter
,但所有相同的技术都适用于通过谓词进行过滤。
我的谓词实现匹配 begin
和 end
的包含范围。如果您的要求略有不同(例如排除范围,结果中不包含 end
),那么您可以相应地调整 apply
的实现。
请注意 JavaDocs 中为 filterKeys
方法 return 编辑的 Multimap
实例记录的一些细节,例如:
The returned multimap is a live view of unfiltered; changes to one affect the other.
...
The resulting multimap's views have iterators that don't support remove()...
...
The returned multimap isn't threadsafe or serializable, even if unfiltered is.
...
Many of the filtered multimap's methods, such as size(), iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the copy.
作为补充说明,通过更改方法签名以使用接受各种 Comparable
类型的泛型,而不仅仅是 LocalDate
,可以使 between
谓词更加灵活。
private static <T extends Comparable<? super T>> Predicate<T> between(final T begin, final T end) {
return new Predicate<T>() {
@Override
public boolean apply(T value) {
return (value.compareTo(begin) >= 0 && value.compareTo(end) <= 0);
}
};
}
我有一个包含由日期和金额组成的付款交易的数据集。我将这些存储在地图数据结构中,日期作为键,金额作为值。
由于每个日期可能有多次付款,我使用 Google Guava 库中的 Multimap。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
Multimap<LocalDate,BigDecimal> payments = ArrayListMultimap.create();
payments.put(LocalDate.parse("12/25/2016",formatter), new BigDecimal("1000"));
payments.put(LocalDate.parse("01/15/2017",formatter), new BigDecimal("250"));
payments.put(LocalDate.parse("01/25/2017",formatter), new BigDecimal("500"));
payments.put(LocalDate.parse("03/20/2017",formatter), new BigDecimal("500"));
payments.put(LocalDate.parse("04/15/2017",formatter), new BigDecimal("1000"));
payments.put(LocalDate.parse("06/15/2017",formatter), new BigDecimal("1000"));
根据日期范围过滤此地图的推荐方法是什么?
例如,显示3/2/2017-3/31/2017之间的条目。
您需要对 Multimap
进行排序,否则您必须全部迭代。还好有这么一个multimap:
ListMultimap<LocalDate, BigDecimal> multimap =
MultimapBuilder.treeKeys().arrayListValues().build();
... fill
// This cast is safe.
SortedMap<LocalDate, Collection<BigDecimal>> asMap =
(SortedMap<LocalDate, Collection<BigDecimal>>) multimap.asMap();
SortedMap<LocalDate, Collection<BigDecimal>> subMap =
asMap.subMap(from, to);
for (Map.Entry<LocalDate, Collection<BigDecimal>> e : subMap.entrySet()) {
...
}
您编码的Multimaps#filterKeys
method allows you to filter an existing Multimap
by keys matching any arbitrary Predicate
。它的 return 值是一个 Multimap
,只包含满足过滤谓词的条目。
首先,让我们定义一个辅助方法,它可以创建一个 Predicate
来检查日期是否在指定范围内。
private static Predicate<LocalDate> between(final LocalDate begin, final LocalDate end) {
return new Predicate<LocalDate>() {
@Override
public boolean apply(LocalDate date) {
return (date.compareTo(begin) >= 0 && date.compareTo(end) <= 0);
}
};
}
之后,您可以使用谓词来过滤您想要的范围。
void testFilterPayments() {
Multimap<LocalDate, BigDecimal> payments = ArrayListMultimap.create();
payments.put(LocalDate.parse("2016-12-25"), new BigDecimal("1000"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("250"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("1250"));
payments.put(LocalDate.parse("2017-01-15"), new BigDecimal("2250"));
payments.put(LocalDate.parse("2017-01-25"), new BigDecimal("500"));
payments.put(LocalDate.parse("2017-03-20"), new BigDecimal("500"));
payments.put(LocalDate.parse("2017-04-15"), new BigDecimal("1000"));
payments.put(LocalDate.parse("2017-06-15"), new BigDecimal("1000"));
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2017-01-01"), LocalDate.parse("2017-04-01"))));
// Output:
// {2017-01-25=[500], 2017-03-20=[500], 2017-01-15=[250, 1250, 2250]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2017-01-01"), LocalDate.parse("2017-01-15"))));
// Output:
// {2017-01-15=[250, 1250, 2250]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2016-01-01"), LocalDate.parse("2017-12-31"))));
// Output:
// {2017-06-15=[1000], 2017-01-25=[500], 2017-03-20=[500], 2016-12-25=[1000], 2017-01-15=[250, 1250, 2250], 2017-04-15=[1000]}
System.out.println(Multimaps.filterKeys(payments,
between(LocalDate.parse("2001-01-01"), LocalDate.parse("2015-12-31"))));
// Output:
// {}
}
我已简化此示例以使用默认日期格式进行解析。您的原始示例似乎使用了自定义 formatter
,但所有相同的技术都适用于通过谓词进行过滤。
我的谓词实现匹配 begin
和 end
的包含范围。如果您的要求略有不同(例如排除范围,结果中不包含 end
),那么您可以相应地调整 apply
的实现。
请注意 JavaDocs 中为 filterKeys
方法 return 编辑的 Multimap
实例记录的一些细节,例如:
The returned multimap is a live view of unfiltered; changes to one affect the other.
...
The resulting multimap's views have iterators that don't support remove()...
...
The returned multimap isn't threadsafe or serializable, even if unfiltered is.
...
Many of the filtered multimap's methods, such as size(), iterate across every key/value mapping in the underlying multimap and determine which satisfy the filter. When a live view is not needed, it may be faster to copy the filtered multimap and use the copy.
作为补充说明,通过更改方法签名以使用接受各种 Comparable
类型的泛型,而不仅仅是 LocalDate
,可以使 between
谓词更加灵活。
private static <T extends Comparable<? super T>> Predicate<T> between(final T begin, final T end) {
return new Predicate<T>() {
@Override
public boolean apply(T value) {
return (value.compareTo(begin) >= 0 && value.compareTo(end) <= 0);
}
};
}