按日期分组项目

Grouping items by date

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

我需要根据用户提供的 "compression" 对它们进行分组。选项为 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 = 一年中的第几天
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)方法调整的