Как сгруппировать строки по неделям, месяцам и кварталам?
Получаю несколько да в String формате . И мне нужно их сгруппировать по неделям, месяцам и кварталам . Как это реализовать на java?
Пробовал таким способом
import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
private Map<Integer, List<String>> dates;
private String Date;
public void DataPick() {
dates = new HashMap<>();
putDataInCollection("2017-01-01");
putDataInCollection("2017-01-01");
putDataInCollection("2017-01-05");
putDataInCollection("2017-01-10");
putDataInCollection("2017-01-12");
putDataInCollection("2017-01-15");
}
@Test
public void putDataInCollection(String order){
Calendar calendar = Calendar.getInstance();
calendar.setTime((java.util.Date) getDate(order));
List<String> datesList ;
if (dates.get(calendar.get(Calendar.WEEK_OF_YEAR))!=null) {
datesList = dates.get(calendar.get(Calendar.WEEK_OF_YEAR));
} else {
datesList = new ArrayList<>();
}
datesList.add(order);
dates.put(calendar.get(Calendar.WEEK_OF_YEAR),datesList);
System.out.println(dates);
}
@Test
public Comparable<java.util.Date> getDate(String order) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date convertedDate = new Date();
try {
convertedDate = dateFormat.parse(Date);
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(convertedDate);
return convertedDate;
}
}
Ответы (2 шт):
Я бы сделал так:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class Main {
private final static DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static void main(String[] args) {
List<String> dates = Arrays.asList("2017-01-01", "2017-01-01", "2017-01-05", "2017-01-10", "2017-03-12", "2017-01-15");
Map<Integer, List<LocalDate>> groupingByMonth = collect(dates, LocalDate::getMonthValue);
Map<Integer, List<LocalDate>> groupingByQuarter = collect(dates, date->date->(date.getMonthValue()-1)/3);
}
public static Map<Integer, List<LocalDate>> collect(Iterable<String> dates, Function<LocalDate, Integer> collector) {
return StreamSupport.stream(dates.spliterator(), false)
.map(date -> LocalDate.parse(date, FORMATTER))
.collect(Collectors.groupingBy(collector));
}
}
Здесь мы получаем результат, сгруппированный по месяцам и кварталам. По неделям допишите сами способом, аналогичным с кварталами, т.е. измените вот эту функцию для подсчета недель : date->(date.getMonthValue()-1)/3
в целом это не сложно, просто вы не сказали, как именно вы хотите считать недели? Если год начинается в среду, то первая неделя это со среды по воскресенье (а может по субботу) или это все же первые 7 дней года?
Для работы с датами уже почти 9 лет рекомендуется использовать Java Date/Time API и соответствующие классы LocalDate вместо java.util.Date и особенно Calendar / SimpleDateFormatter.
Так как требуется получить группировку по разным ключам, то можно написать общий метод, который принимает на вход список дат и некую функцию для вычисления ключа (месяц, квартал, неделя).
Реализация с использованием Stream API:
private static Map<Integer, List<String>> groupDatesBy(List<String> dates, Function<String, Integer> keyMapper) {
return dates.stream().collect(Collectors.groupingBy(keyMapper));
}
Вариант без стримов с использованием Map::computeIfAbsent:
private static Map<Integer, List<String>> groupDatesBy(List<String> dates, Function<String, Integer> keyMapper) {
Map<Integer, List<String>> result = new LinkedHashMap<>();
for (String date : dates) {
result.computeIfAbsent(keyMapper.apply(date), k -> new ArrayList<>()).add(date);
}
return result;
}
Группировка по месяцам тривиальна, функция получения месяца из строковой даты выглядит как d -> LocalDate.parse(d).getMonthValue():
public static Map<Integer, List<String>> datesByMonth(List<String> dates) {
return groupDatesBy(dates, d -> LocalDate.parse(d).getMonthValue());
}
Аналогично, номер квартала следует правильно вычислить по номеру месяца следующим образом d -> (LocalDate.parse(d).getMonthValue() - 1) / 3 + 1 (делить следует на количество месяцев в квартале, а не на количество кварталов в году):
public static Map<Integer, List<String>> datesByQuarter(List<String> dates) {
return groupDatesBy(dates, d -> (LocalDate.parse(d).getMonthValue() - 1) / 3 + 1);
}
А вот группировку по номеру недели можно получить при помощи класса WeekFields, в котором определяется первый день недели и минимальное количество дней в первой неделе года (если меньше минимума, будет считаться как 52 неделя прошлого года для метода WeekFields::weekOfWeekBasedYear или 0 для WeekFields::weekOfYear ).
public static Map<Integer, List<String>> datesByWeek(WeekFields weekFields, List<String> dates) {
return groupDatesBy(dates, d -> LocalDate.parse(d).get(weekFields.weekOfYear()));
}
Тесты:
var byMonth = datesByMonth(Arrays.asList("2017-01-02", "2017-01-31", "2017-02-18", "2017-03-30"));
System.out.println("Month: " + byMonth);
var byQuarter = datesByQuarter(Arrays.asList("2017-01-02", "2017-03-31", "2017-04-18", "2017-06-30", "2017-07-25", "2017-08-31", "2017-09-25", "2017-11-03", "2017-12-31"));
System.out.println("Quart: " + byQuarter);
var dates = Arrays.asList("2017-01-01", "2017-01-01", "2017-01-05", "2017-01-10", "2017-01-12", "2017-01-15", "2017-01-17");
var weekFields = WeekFields.of(DayOfWeek.SUNDAY, 1);// WeekFields.SUNDAY_START;
var byWeekSun = datesByWeek(weekFields, dates);
System.out.println("SUN: " + byWeekSun);
var byWeekIso = datesByWeek(WeekFields.ISO, dates);
System.out.println("ISO: " + byWeekIso);
Результаты:
Month: {1=[2017-01-02, 2017-01-31], 2=[2017-02-18], 3=[2017-03-30]}
Quart: {1=[2017-01-02, 2017-03-31], 2=[2017-04-18, 2017-06-30], 3=[2017-07-25, 2017-08-31, 2017-09-25], 4=[2017-11-03, 2017-12-31]}
SUN: {1=[2017-01-01, 2017-01-01, 2017-01-05], 2=[2017-01-10, 2017-01-12], 3=[2017-01-15, 2017-01-17]}
ISO: {0=[2017-01-01, 2017-01-01], 1=[2017-01-05], 2=[2017-01-10, 2017-01-12, 2017-01-15], 3=[2017-01-17]}