按日期分组项目
Grouping items by date
我的列表中有项目,项目有一个字段,显示项目的创建日期。
我需要根据用户提供的 "compression" 对它们进行分组。选项为 Day
、Week
、Month
和 Year
。
如果用户选择day
压缩,我需要将我的项目分组,以便将在同一天创建的项目分组。在我上面的例子中,只有项目 1 和项目 2 是在同一天创建的。其他人也是组,但他们只有一个项目,因为在他们那一天,只创建了一个项目。
{{item1, item2}, {item3}, {item4}, {item5}, {item6}, {item7}}
如果用户选择week
:
{{item1, item2, item3, item4}, {item5}, {item6}, {item7}}
如果用户选择month
:
{{item1, item2, item3, item4, item5}, {item6}, {item7}}
如果用户选择year
:
{{item1, item2, item3, item4, item5, item6}, {item7}}
创建组后,项目的日期并不重要。我的意思是密钥可以是任何东西,只要创建了组即可。
在使用Map
的情况下,我认为键如下:
day
= 一年中的第几天
week
= 一年中的第几周
month
= 一年中的月份
year
= 年
这个问题的最佳解决方案是什么?我什至无法启动它,我想不出除了迭代之外的解决方案。
像这样使用 HashMap
<Integer,ArrayList<Date>>
1. set filter=DAY/MONTH/YEAR
2.Iterate the date_obj
3.Use Calendar package to get a variable val=Calendar.get(filter)
4. If hashmap.keyset().contains(val)
hashmap.get(val).add(date_obj.date)
Else
hashmap.put(val,new ArrayList<date_obj>());
您可以通过 java 8 个流获得您描述的行为:
Map<LocalDate, List<Data>> byYear = data.stream()
.collect(groupingBy(d -> d.getDate().withMonth(1).withDayOfMonth(1)));
Map<LocalDate, List<Data>> byMonth = data.stream()
.collect(groupingBy(d -> d.getDate().withDayOfMonth(1)));
Map<LocalDate, List<Data>> byWeek = data.stream()
.collect(groupingBy(d -> d.getDate().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))));
Map<LocalDate, List<Data>> byDay = data.stream()
.collect(groupingBy(d -> d.getDate()));
groupingBy and collect. In all 4 cases LocalDate 的文档用作密钥。为了适当分组,修改为所有日期同月同日或同日异月或同月同日(星期一),这样分组规则就很明显了。您数据中的日期未被修改的唯一关键。这将认为当年份相同时月份相同,当完整日期相同时日期相同。
例如,当按月分组时,这些日期将具有相同的键:
01/01/2017 --> key 01/01/2017
04/01/2017 --> key 01/01/2017
05/01/2017 --> key 01/01/2017
并且当按周分组时,这些日期将具有相同的键(日期是前一个星期一):
04/01/2017 --> key 02/01/2017
05/01/2017 --> key 02/01/2017
例如,您可能想要按月份的同一天分组,而不考虑年份和月份。你会这样实现它:
Map<Integer, List<Data>> byDayOfMonth = data.stream()
.collect(groupingBy(d -> d.getDate().getDayOfMonth()));
这会将 01/01/2017 与 01/10/2017 组合在一起,然后将 05/01/2017 与 05/04/2018 组合在一起
假设您的项目元素具有这些属性:
private String item;
private LocalDate date;
你可以这样做:
ArrayList<Element> list = new ArrayList<>();
list.add(new Element("item 1", LocalDate.of(2017, 01, 01)));
list.add(new Element("item 2", LocalDate.of(2017, 01, 01)));
WeekFields weekFields = WeekFields.of(Locale.getDefault());
String userChoice = new Scanner(System.in).nextLine();
Map<Integer, List<Element>> map;
switch (userChoice) {
case "day":
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().getDayOfMonth()));
break;
case "week":
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().get(weekFields.weekOfWeekBasedYear())));
break;
case "month":
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().getMonthValue()));
break;
case "year":
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().getYear()));
break;
default:
break;
}
根据用户的选择,地图将按照他的选择映射项目
详情:
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().getYear()));
这将遍历列表的项目,并查看 year of the date of the item
以将它们分组
我唯一要注意的细节是星期的定义。我假设您的一周从星期日开始,并且是否必须至少有 1 天才能被视为第一周。 (ISO 8601 规定一周从星期一开始,并且必须至少有 4 天才能被视为第一周 - 如果天数较少,则被视为第零周)。您可以查看 javadoc 了解有关周定义的更多详细信息。
为了获得本周的定义,我使用 java.time.temporal.WeekFields
class。我正在使用 of
方法,该方法显式使用一周的第一天和第一周的最少天数(如果我使用采用 Locale
的版本,结果可能会有所不同,具体取决于系统的默认语言环境)。
我还为压缩类型创建了一个枚举,但这是可选的:
enum CompressionType {
DAY, WEEK, MONTH, YEAR;
}
无论如何,我使用压缩类型来知道将使用哪个字段来对日期进行分组。然后我使用 Collectors.groupingBy
进行分组:
// assuming you have a list of dates
List<LocalDate> dates = new ArrayList<>();
dates.add(LocalDate.of(2017, 1, 1));
dates.add(LocalDate.of(2017, 1, 1));
dates.add(LocalDate.of(2017, 1, 4));
dates.add(LocalDate.of(2017, 1, 5));
dates.add(LocalDate.of(2017, 1, 29));
dates.add(LocalDate.of(2017, 10, 1));
dates.add(LocalDate.of(2018, 4, 5));
CompressionType compression = // get CompressionType from user input
final TemporalField groupField;
switch (compression) {
case DAY:
groupField = ChronoField.DAY_OF_YEAR;
break;
case WEEK:
// week starts at Sunday, minimum of 1 day in the first week
groupField = WeekFields.of(DayOfWeek.SUNDAY, 1).weekOfWeekBasedYear();
break;
case MONTH:
groupField = ChronoField.MONTH_OF_YEAR;
break;
case YEAR:
groupField = ChronoField.YEAR;
break;
default:
groupField = null;
}
if (groupField != null) {
Map<Integer, List<LocalDate>> result = dates.stream().collect(Collectors.groupingBy(d -> d.get(groupField)));
}
我会在分类器上使用 Collectors.groupingBy
和调整后的 LocalDate
,这样具有相似日期的项目(根据用户给出的 compression ) 组合在一起。
为此,首先创建以下 Map
:
static final Map<String, TemporalAdjuster> ADJUSTERS = new HashMap<>();
ADJUSTERS.put("day", TemporalAdjusters.ofDateAdjuster(d -> d)); // identity
ADJUSTERS.put("week", TemporalAdjusters.previousOrSame(DayOfWeek.of(1)));
ADJUSTERS.put("month", TemporalAdjusters.firstDayOfMonth());
ADJUSTERS.put("year", TemporalAdjusters.firstDayOfYear());
注意:对于 "day"
,正在使用一个 TemporalAdjuster
让日期保持不变。
接下来,使用用户给出的 compression
动态 select 如何对您的项目列表进行分组:
Map<LocalDate, List<Item>> result = list.stream()
.collect(Collectors.groupingBy(item -> item.getCreationDate()
.with(ADJUSTERS.get(compression))));
LocalDate
是通过LocalDate.with(TemporalAdjuster)
方法调整的
我的列表中有项目,项目有一个字段,显示项目的创建日期。
我需要根据用户提供的 "compression" 对它们进行分组。选项为 Day
、Week
、Month
和 Year
。
如果用户选择day
压缩,我需要将我的项目分组,以便将在同一天创建的项目分组。在我上面的例子中,只有项目 1 和项目 2 是在同一天创建的。其他人也是组,但他们只有一个项目,因为在他们那一天,只创建了一个项目。
{{item1, item2}, {item3}, {item4}, {item5}, {item6}, {item7}}
如果用户选择week
:
{{item1, item2, item3, item4}, {item5}, {item6}, {item7}}
如果用户选择month
:
{{item1, item2, item3, item4, item5}, {item6}, {item7}}
如果用户选择year
:
{{item1, item2, item3, item4, item5, item6}, {item7}}
创建组后,项目的日期并不重要。我的意思是密钥可以是任何东西,只要创建了组即可。
在使用Map
的情况下,我认为键如下:
day
= 一年中的第几天
week
= 一年中的第几周
month
= 一年中的月份
year
= 年
这个问题的最佳解决方案是什么?我什至无法启动它,我想不出除了迭代之外的解决方案。
像这样使用 HashMap
<Integer,ArrayList<Date>>
1. set filter=DAY/MONTH/YEAR
2.Iterate the date_obj
3.Use Calendar package to get a variable val=Calendar.get(filter)
4. If hashmap.keyset().contains(val)
hashmap.get(val).add(date_obj.date)
Else
hashmap.put(val,new ArrayList<date_obj>());
您可以通过 java 8 个流获得您描述的行为:
Map<LocalDate, List<Data>> byYear = data.stream()
.collect(groupingBy(d -> d.getDate().withMonth(1).withDayOfMonth(1)));
Map<LocalDate, List<Data>> byMonth = data.stream()
.collect(groupingBy(d -> d.getDate().withDayOfMonth(1)));
Map<LocalDate, List<Data>> byWeek = data.stream()
.collect(groupingBy(d -> d.getDate().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))));
Map<LocalDate, List<Data>> byDay = data.stream()
.collect(groupingBy(d -> d.getDate()));
groupingBy and collect. In all 4 cases LocalDate 的文档用作密钥。为了适当分组,修改为所有日期同月同日或同日异月或同月同日(星期一),这样分组规则就很明显了。您数据中的日期未被修改的唯一关键。这将认为当年份相同时月份相同,当完整日期相同时日期相同。
例如,当按月分组时,这些日期将具有相同的键:
01/01/2017 --> key 01/01/2017
04/01/2017 --> key 01/01/2017
05/01/2017 --> key 01/01/2017
并且当按周分组时,这些日期将具有相同的键(日期是前一个星期一):
04/01/2017 --> key 02/01/2017
05/01/2017 --> key 02/01/2017
例如,您可能想要按月份的同一天分组,而不考虑年份和月份。你会这样实现它:
Map<Integer, List<Data>> byDayOfMonth = data.stream()
.collect(groupingBy(d -> d.getDate().getDayOfMonth()));
这会将 01/01/2017 与 01/10/2017 组合在一起,然后将 05/01/2017 与 05/04/2018 组合在一起
假设您的项目元素具有这些属性:
private String item;
private LocalDate date;
你可以这样做:
ArrayList<Element> list = new ArrayList<>();
list.add(new Element("item 1", LocalDate.of(2017, 01, 01)));
list.add(new Element("item 2", LocalDate.of(2017, 01, 01)));
WeekFields weekFields = WeekFields.of(Locale.getDefault());
String userChoice = new Scanner(System.in).nextLine();
Map<Integer, List<Element>> map;
switch (userChoice) {
case "day":
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().getDayOfMonth()));
break;
case "week":
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().get(weekFields.weekOfWeekBasedYear())));
break;
case "month":
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().getMonthValue()));
break;
case "year":
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().getYear()));
break;
default:
break;
}
根据用户的选择,地图将按照他的选择映射项目
详情:
map = list.stream().collect(Collectors.groupingBy(element
-> element.getDate().getYear()));
这将遍历列表的项目,并查看 year of the date of the item
以将它们分组
我唯一要注意的细节是星期的定义。我假设您的一周从星期日开始,并且是否必须至少有 1 天才能被视为第一周。 (ISO 8601 规定一周从星期一开始,并且必须至少有 4 天才能被视为第一周 - 如果天数较少,则被视为第零周)。您可以查看 javadoc 了解有关周定义的更多详细信息。
为了获得本周的定义,我使用 java.time.temporal.WeekFields
class。我正在使用 of
方法,该方法显式使用一周的第一天和第一周的最少天数(如果我使用采用 Locale
的版本,结果可能会有所不同,具体取决于系统的默认语言环境)。
我还为压缩类型创建了一个枚举,但这是可选的:
enum CompressionType {
DAY, WEEK, MONTH, YEAR;
}
无论如何,我使用压缩类型来知道将使用哪个字段来对日期进行分组。然后我使用 Collectors.groupingBy
进行分组:
// assuming you have a list of dates
List<LocalDate> dates = new ArrayList<>();
dates.add(LocalDate.of(2017, 1, 1));
dates.add(LocalDate.of(2017, 1, 1));
dates.add(LocalDate.of(2017, 1, 4));
dates.add(LocalDate.of(2017, 1, 5));
dates.add(LocalDate.of(2017, 1, 29));
dates.add(LocalDate.of(2017, 10, 1));
dates.add(LocalDate.of(2018, 4, 5));
CompressionType compression = // get CompressionType from user input
final TemporalField groupField;
switch (compression) {
case DAY:
groupField = ChronoField.DAY_OF_YEAR;
break;
case WEEK:
// week starts at Sunday, minimum of 1 day in the first week
groupField = WeekFields.of(DayOfWeek.SUNDAY, 1).weekOfWeekBasedYear();
break;
case MONTH:
groupField = ChronoField.MONTH_OF_YEAR;
break;
case YEAR:
groupField = ChronoField.YEAR;
break;
default:
groupField = null;
}
if (groupField != null) {
Map<Integer, List<LocalDate>> result = dates.stream().collect(Collectors.groupingBy(d -> d.get(groupField)));
}
我会在分类器上使用 Collectors.groupingBy
和调整后的 LocalDate
,这样具有相似日期的项目(根据用户给出的 compression ) 组合在一起。
为此,首先创建以下 Map
:
static final Map<String, TemporalAdjuster> ADJUSTERS = new HashMap<>();
ADJUSTERS.put("day", TemporalAdjusters.ofDateAdjuster(d -> d)); // identity
ADJUSTERS.put("week", TemporalAdjusters.previousOrSame(DayOfWeek.of(1)));
ADJUSTERS.put("month", TemporalAdjusters.firstDayOfMonth());
ADJUSTERS.put("year", TemporalAdjusters.firstDayOfYear());
注意:对于 "day"
,正在使用一个 TemporalAdjuster
让日期保持不变。
接下来,使用用户给出的 compression
动态 select 如何对您的项目列表进行分组:
Map<LocalDate, List<Item>> result = list.stream()
.collect(Collectors.groupingBy(item -> item.getCreationDate()
.with(ADJUSTERS.get(compression))));
LocalDate
是通过LocalDate.with(TemporalAdjuster)
方法调整的