Group items by date

I have items in my list, and the items have a field that shows the date the item was created.

enter image description here

and I need to group them based on the "compression" that the user gives. Possible values ​​are Day , Week , Month and Year .

If the user selects Day compression, I need to group my elements so that the elements created on the same day are grouped. In my example above, on the same day only points 1 and 2 are created. The rest are also groups, but they will only have one element, because only one element is created at a time.

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

If the user selects Week :

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

If the user selects Month :

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

If the user selects Year :

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

After creating the groups, the date of the elements is not important. I mean, the key can be anything if groups are created.

In the case of using Map , I thought the following keys:

Day = day of the year
Week = week of the year
Month = month of the year
Year = year

What would be the best solution to this problem? I couldn’t even start it, I can’t think of a solution other than iteration.

+5
source share
5 answers

I would use Collectors.groupingBy with the adjusted LocalDate in the classifier, so that elements with similar dates (according to the compression provided by the user) are grouped together.

To do this, first create the following 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: TemporalAdjuster used for "day" , which allows you to use the untouched date.

Next, use user-defined compression to dynamically choose how to group the list of items:

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

LocalDate configured using the LocalDate.with(TemporalAdjuster) method.

+12
source

You can get a description of the behavior using java 8 threads:

 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 groupingBy and collect . In all four cases, LocalDate is used as the key. To group accordingly, it changes so that all dates have the same month and day or the same day, but another month or the same month and the same day of the week (Monday), which leads to obvious grouping rules. The date in your data is not changed only by a key. This will assume that the month is the same when the year is the same and the day is the same when the full date is the same.

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

01/01/2017 β†’ key 01/01/2017
01/04/2017 β†’ key 01/01/2017
01/05/2017 β†’ key 01/01/2017

and when grouping by week, these dates will have the same key (the date precedes Monday):

01/04/2017 β†’ key 02/01/2017
01/05/2017 β†’ key 02/01/2017

You may want to instead group on the same day of the month, for example, regardless of year and month. You will achieve this as follows:

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

which came together 01/01/2017 from 01/10/2017, and then 05/01/2017 from 05/04/2018

+2
source

Use a HashMap like this.

  <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>()); 
+1
source

Assuming your item element has the following attributes:

 private String item; private LocalDate date; 

You can do the following:

 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's choice, the map will be displayed as a display of the elements following his choice.


Details:

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

This will navigate through the list item and look at the year of the date of the item to group them using

+1
source

The only detail that I would pay more attention to is the definition of the week. I assume that your week starts on Sunday, and if it should be at least 1 day in advance, to be considered the first week. (ISO 8601 states that the week starts on Monday, and it must have at least 4 days to be considered the first week - if it has fewer days, it is considered zero week). Learn more about week definitions at javadoc .

To get this week's definition, I use the java.time.temporal.WeekFields class. I use the of method, which explicits uses the first day of the week and the minimum number of days in the first week (if I use the version that accepts Locale , the results may differ depending on the default locale for the system).

I also created an enumeration for the compression type, but this is optional:

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

In any case, I use the compression type to find out which field will be used to group dates. Then I used Collectors.groupingBy to group:

 // 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))); } 
+1
source

Source: https://habr.com/ru/post/1269759/


All Articles