Как сгруппировать строки по неделям, месяцам и кварталам?

Рейтинг: -2Ответов: 2Опубликовано: 07.02.2023

Получаю несколько да в 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 дней года?

▲ 0

Для работы с датами уже почти 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]}