按日期对项目进行分组

我的列表中有项目,项目有一个字段,显示项目的创建日期。

在此处输入图像描述

我需要根据用户提供的“压缩”对它们进行分组。 选项包括DayWeekMonthYear

如果用户选择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 =一年中的day一天
week =一年中的一周
month =一年中的月份
year =年

什么是这个问题的最佳解决方案? 我甚至无法启动它,除了迭代之外我无法想到解决方案。

我会在分类Collectors.groupingBy上使用Collectors.groupingBy和调整后的LocalDate ,以便将具有相似日期的项目(根据用户给出的压缩 )组合在一起。

为此,首先创建以下Map

 static final Map 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来动态选择如何对项列表进行分组:

 Map> result = list.stream() .collect(Collectors.groupingBy(item -> item.getCreationDate() .with(ADJUSTERS.get(compression)))); 

LocalDate通过LocalDate.with(TemporalAdjuster)方法进行调整。

您可以获得使用java 8流描述的行为:

 Map> byYear = data.stream() .collect(groupingBy(d -> d.getDate().withMonth(1).withDayOfMonth(1))); Map> byMonth = data.stream() .collect(groupingBy(d -> d.getDate().withDayOfMonth(1))); Map> byWeek = data.stream() .collect(groupingBy(d -> d.getDate().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)))); Map> byDay = data.stream() .collect(groupingBy(d -> d.getDate())); 

用于分组的文档和收集 。 在所有4种情况下, LocalDate用作密钥。 为了进行适当的分组,对其进行修改,以使所有日期具有相同的月份和日期或同一天但不同的月份或同一个月以及同一天(星期一),从而产生明显的分组规则。 数据中的日期不会仅修改密钥。 这将考虑当年份相同时月份相同,并且当完整日期相同时日期相同。

例如,按月分组时,这些日期将具有相同的键:

2017/01/01 – >关键01/01/2017
04/01/2017 – >关键01/01/2017
05/01/2017 – >关键01/01/2017

当按周分组时,这些日期将具有相同的密钥(日期是前一个星期一):

04/01/2017 – >关键02/01/2017
05/01/2017 – >关键02/01/2017

例如,您可能希望按月份的同一天进行分组,而不管年份和月份。 你会像这样实现它:

 Map> byDayOfMonth = data.stream() .collect(groupingBy(d -> d.getDate().getDayOfMonth())); 

2017年1月1日至2017年5月5日和05/04/20一起组合在一起

我更关注的唯一细节是周定义。 我假设你的一周从星期日开始,如果它必须至少有一天至少被认为是第一周。 (ISO 8601规定,一周从星期一开始,必须至少有4天才能被视为第一周 – 如果它的天数较少,则认为是零周)。 您可以查看javadoc以获取有关周定义的更多详细信息。

要获得本周定义,我正在使用java.time.temporal.WeekFields类。 我正在使用explicits使用的方法使用一周的第一天和第一周的最小天数(如果我使用采用Locale的版本,结果可能会因系统的默认语言环境而异)。

我还为压缩类型创建了一个枚举,但这是可选的:

 enum CompressionType { DAY, WEEK, MONTH, YEAR; } 

无论如何,我使用压缩类型来知道将使用哪个字段来分组日期。 然后我使用Collectors.groupingBy进行分组:

 // assuming you have a list of dates List 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> result = dates.stream().collect(Collectors.groupingBy(d -> d.get(groupField))); } 

像这样使用HashMap

  > 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()); 

假设您的item元素具有以下属性:

 private String item; private LocalDate date; 

你可以这样做:

 ArrayList 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> 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 itemyear of the date of the item进行分组