java 按日期分组项目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45060886/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 08:29:03  来源:igfitidea点击:

Grouping items by date

javajava-8java-streamjava-time

提问by drJava

I have items in my list and the items have a field, which shows the creation date of the item.

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

enter image description here

在此处输入图片说明

and I need to group them based on a "compression", which the user gives. The options are Day, Week, Monthand Year.

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

If the user selects daycompression, I need to group my items as such that the items, which are created in the same day, will be groupped. In my example above, only item 1 and item 2 are created in the same day. The others are also groups but they will have only one item because at their day, only one item is created.

如果用户选择day压缩,我需要对我的项目进行分组,以便对同一天创建的项目进行分组。在我上面的例子中,只有项目 1 和项目 2 是在同一天创建的。其他人也是组,但他们只有一个项目,因为在他们的日子里,只有一个项目被创建。

{{item1, item2}, {item3}, {item4}, {item5}, {item6}, {item7}}

If the user selects week:

如果用户选择week

{{item1, item2, item3, item4}, {item5}, {item6}, {item7}}

If the user selects month:

如果用户选择month

{{item1, item2, item3, item4, item5}, {item6}, {item7}}

If the user selects year:

如果用户选择year

{{item1, item2, item3, item4, item5, item6}, {item7}}

After groups are created, the date of the items are not important. I mean the key can be anything, as long as the groups are created.

创建组后,项目的日期并不重要。我的意思是,只要创建了组,密钥就可以是任何东西。

In case of usage of Map, I thought as the keys as follow:

在使用 的情况下Map,我认为关键如下:

day= day of the year
week= week of the year
month= month of the year
year= year

day= 一年中的某一天 = 一年中的
week一周= 一年中的
month一个月
year= 年

What would be the best solution to this problem? I could not even start it an I cannot think of a solution other than iteration.

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

回答by Federico Peralta Schaffner

I would use Collectors.groupingBywith an adjusted LocalDateon the classifier, so that items with similar dates (according to the compressiongiven by the user) are grouped together.

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

For this, first create the following Map:

为此,首先创建以下内容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());

Note: for "day", a TemporalAdjusterthat lets the date untouched is being used.

注意: for "day"TemporalAdjuster正在使用让日期不变的a 。

Next, use the compressiongiven by the user to dynamically select how to group your list of items:

接下来,使用compression用户给出的 动态选择如何对项目列表进行分组:

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

The LocalDateis adjusted by means of the LocalDate.with(TemporalAdjuster)method.

LocalDate是由进行调整LocalDate.with(TemporalAdjuster)的方法。

回答by Manos Nikolaidis

You can get the behaviour you describe with java 8 streams:

您可以使用 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()));

Docs for groupingByand collect. In all 4 cases LocalDateis used as key. To group appropriately, it is modified so that all dates have the same month and day or same day but different month or same month and same day of week (Monday) which leads to obvious grouping rules. The date in your data is not modified only the key. This will consider that the month is the same when also the year is the same and the day is the same when the full date is the same.

groupingBycollect 的文档。在所有 4 种情况下LocalDate都用作键。为了适当地分组,它被修改为使所有日期具有相同的月和日或同一天,但不同的月或同月和同一周的同一天(星期一)导致明显的分组规则。您数据中的日期不是修改唯一的键。这将考虑当年相同时月份相同,当完整日期相同时日期相同。

For example when grouping by month these dates will have the same key:

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

01/01/2017 --> key 01/01/2017
04/01/2017 --> key 01/01/2017
05/01/2017 --> key 01/01/2017

01/01/2017 --> 钥匙 01/01/2017
04/01/2017 --> 钥匙 01/01/2017
05/01/2017 --> 钥匙 01/01/2017

and when grouping by week these dates will have the same key (date is previous monday):

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

04/01/2017 --> key 02/01/2017
05/01/2017 --> key 02/01/2017

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

You may want instead to group by same day of month for example regardless of year and month. You would achieve it like this:

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

Map<Integer, List<Data>> byDayOfMonth = data.stream()
        .collect(groupingBy(d -> d.getDate().getDayOfMonth()));

Which would group together 01/01/2017 with 01/10/2017 and then 05/01/2017 with 05/04/2018

将 01/01/2017 与 01/10/2017 组合在一起,然后将 05/01/2017 与 05/04/2018 组合在一起

回答by Joey Pinto

Use a HashMap like this

使用这样的 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>());

回答by Joey Pinto

The only detail I'd pay more attention is the week definition. I'm assuming that your week starts at Sunday and if it must have at least a minimum of 1 day to be considered the first week. (ISO 8601 states that a week starts at Monday and it must have at least 4 days to be considered the first week - if it has fewer days, it's considered week zero). You can check the javadocfor more details about week definitions.

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

To get this week definition, I'm using java.time.temporal.WeekFieldsclass. I'm using the ofmethod that explicits uses the first day of week and the minimum number of days in the first week (if I use the version that takes a Locale, results might differ depending on the system's default locale).

为了获得本周的定义,我正在使用java.time.temporal.WeekFields类。我使用的of方法是显式使用一周的第一天和第一周的最少天数(如果我使用带有 的版本Locale,结果可能会因系统的默认语言环境而异)。

I also created an enum for the compression type, but that's optional:

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

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

Anyway, I use the compression type to know which field will be used to group the dates. Then I used Collectors.groupingByto do the grouping:

无论如何,我使用压缩类型来知道将使用哪个字段对日期进行分组。然后我用来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)));
}

回答by azro

Assuming your item element have these attributes :

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

private String item;
private LocalDate date;

You can do like this :

你可以这样做:

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;
}

Depending on the user choice, the map would result as mapping the items following his choice

根据用户的选择,地图将按照他的选择映射项目



Details :

细节 :

map = list.stream().collect(Collectors.groupingBy(element 
                                        -> element.getDate().getYear()));

This will iterate over the item of the list, and look at the year of the date of the itemto group them by it

这将遍历列表的项目,并查看以将year of the date of the item它们分组