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
Grouping items by date
提问by drJava
I have items in my list and the items have a field, which shows the creation date of the item.
我的列表中有项目,项目有一个字段,显示项目的创建日期。
and I need to group them based on a "compression", which the user gives. The options are Day
, Week
, Month
and Year
.
我需要根据用户提供的“压缩”对它们进行分组。选项包括Day
,Week
,Month
和Year
。
If the user selects day
compression, 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 yearweek
= week of the yearmonth
= month of the yearyear
= 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.groupingBy
with an adjusted LocalDate
on 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 TemporalAdjuster
that lets the date untouched is being used.
注意: for "day"
,TemporalAdjuster
正在使用让日期不变的a 。
Next, use the compression
given 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 LocalDate
is 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.
groupingBy和collect 的文档。在所有 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.WeekFields
class. I'm using the of
method 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.groupingBy
to 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 item
to group them by it
这将遍历列表的项目,并查看以将year of the date of the item
它们分组