it-swarm.com.ru

Группировка товаров по дате

У меня есть элементы в моем списке, и у элементов есть поле, в котором отображается дата создания элемента.

 enter image description here

и мне нужно сгруппировать их на основе «сжатия», которое дает пользователь. Возможные варианты: Day, Week, Month и Year.

Если пользователь выбирает сжатие 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 = год

Что было бы лучшим решением этой проблемы? Я не мог даже начать это, я не могу думать о решении кроме итерации. 

7
drJava

Я бы использовал Collectors.groupingBy с откорректированным LocalDate в классификаторе, чтобы элементы с похожими датами (в соответствии с заданным пользователем сжатием) группировались вместе.

Для этого сначала создайте следующую 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, заданный пользователем, чтобы динамически выбрать, как сгруппировать ваш список элементов:

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

LocalDate настраивается с помощью метода LocalDate.with(TemporalAdjuster) .

18
Federico Peralta Schaffner

Вы можете получить поведение, которое вы описываете с потоками 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 и собирать . Во всех 4 случаях LocalDate используется в качестве ключа. Для соответствующей группировки она модифицируется таким образом, чтобы все даты имели один и тот же месяц и день или один и тот же день, но разные месяц или один и тот же месяц и один и тот же день недели (понедельник), что приводит к очевидным правилам группировки. Дата в ваших данных не модифицируется только ключом. При этом будет учитываться, что месяц совпадает, когда год совпадает, а день совпадает, когда полная дата совпадает.

Например, при группировке по месяцам эти даты будут иметь одинаковый ключ:

01.01.2017 -> ключ 01.01.2017
01.04.2017 -> ключ 01.01.2017
01.05.2017 -> ключ 01.01.2017 

и при группировке по неделям эти даты будут иметь одинаковый ключ (дата - предыдущий понедельник):

01.04.2017 -> ключ 02.01.2017
01.05.2017 -> ключ 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.2017

2
Manos Nikolaidis

Единственная деталь, на которую я бы обратил больше внимания, это определение недели. Я предполагаю, что ваша неделя начинается в воскресенье, и если она должна иметь как минимум 1 день, чтобы считаться первой неделей. (ISO 8601 гласит, что неделя начинается в понедельник, и она должна иметь как минимум 4 дня, чтобы считаться первой неделей - если у нее меньше дней, она считается нулевой). Вы можете проверить javadoc для более подробной информации об определениях недели.

Чтобы получить определение этой недели, я использую класс Java.time.temporal.WeekFields. Я использую метод 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)));
}
2
user7605325

Используйте 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>());
1
Joey Pinto

Предполагая, что ваш элемент item имеет эти атрибуты: 

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
azro