Разделить элементы по условиям
Есть класс Item, описывающий записи в БД.
Также есть коллекция List<Item>, хранящая список таки объектов. Записи группируются по нескольким полям:
record MyKey(long id, String city, OrderStatus status) {
public MyKey(Item item) {
this(item.getId(), item.getCity(), item.getOrderStatus());
}
}
public static Map<MyKey, List<Item>> groupBy(List<Item> list) {
Map<MyKey, List<Item>> map = new LinkedHashMap<>();
list.forEach(item -> map
.computeIfAbsent(new MyKey(item), k -> new ArrayList<>())
.add(item)
);
// отладочный вывод
map.forEach((key, val) -> {
System.out.println(key);
val.forEach(item -> System.out.println("\t" + item));
});
return map;
}
Добавилось поле date, которая хранит дату заказа. Как сгруппировать элементы так, чтобы в одной группе были заказы, у которых разница минимальной и максимальной даты не превышали 10 дней?
Ответы (2 шт):
Автор решения: tym32167
→ Ссылка
Напишу код группировки на C#, на java сами переведете.
Допустим, есть класс
class Item
{
public DateTime Date { get; set; }
}
и есть сортированный по дате массив
var items = new[] {
new Item(){Date = DateTime.Now.Date },
new Item(){Date = DateTime.Now.Date.AddDays(7) },
new Item(){Date = DateTime.Now.Date.AddDays(14) },
new Item(){Date = DateTime.Now.Date.AddDays(21) },
new Item(){Date = DateTime.Now.Date.AddDays(28) },
new Item(){Date = DateTime.Now.Date.AddDays(35) },
new Item(){Date = DateTime.Now.Date.AddDays(42) },
};
Тогда алгоритм сводится к тому, чтобы пройти по массиву и закинуть группы в список списков
var dates = new List<List<Item>>();
int ind = 0;
while(ind < items.Length)
{
var list = new List<Item>();
list.Add(items[ind]);
ind++;
while(ind < items.Length && (items[ind].Date - list[0].Date).TotalDays <10)
{
list.Add(items[ind]);
ind++;
}
dates.Add(list);
}
Вывод на печать
foreach(var group in dates)
{
foreach(var item in group)
{
Console.WriteLine(item.Date.ToString("dd.MM.yyyy"));
}
Console.WriteLine("--------------");
}
Вывод в консоли
22.03.2023
29.03.2023
--------------
05.04.2023
12.04.2023
--------------
19.04.2023
26.04.2023
--------------
03.05.2023
--------------
Автор решения: Nowhere Man
→ Ссылка
- Ищете заказ с минимальной датой, при помощи того же Stream API
Stream::min(Comparator<T> cmp) - Проходите по списку и разбиваете его на две части, например при помощи уже упоминавшегося ранее
Collectors::partitioningBy
private static Map<Boolean, List<Item>> byDate(List<Item> items, int days) {
LocalDate minDate = items.stream()
.map(Item::getDate) // Stream<LocalDate>
.min(Comparator.naturalOrder()) // Optional<LocalDate>
.get();
return items.stream()
.collect(Collectors.partitioningBy(
item -> ChronoUnit.DAYS.between(minDate, item.getDate()) < days
));
}
Разбиение по количеству дней прошедших от начальной даты:
private static Map<Integer, List<Item>> byDate(List<Item> items, int days) {
LocalDate minDate = items.stream()
.map(Item::getDate) // Stream<LocalDate>
.min(Comparator.naturalOrder()) // Optional<LocalDate>
.get();
return items.stream()
.collect(Collectors.groupingBy(
item -> (int) ChronoUnit.DAYS.between(minDate, item.getDate()) / days
));
}
List<Item> items = Arrays.asList(
new Item(1, LocalDate.of(2023, 3, 1)),
new Item(2, LocalDate.of(2023, 3, 14)),
new Item(3, LocalDate.of(2023, 3, 19)),
new Item(4, LocalDate.of(2023, 3, 30))
);
var groups = byDate(items, 10);
groups.forEach((k, v) -> System.out.println(k + ": " + v));
0: [Item[id=1, date=2023-03-01]]
1: [Item[id=2, date=2023-03-14], Item[id=3, date=2023-03-19]]
2: [Item[id=4, date=2023-03-30]]