Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find least dates of each month from given set of dates

Tags:

java

date

sorting

I need to extract least dates from given dates array for every mentioned month.

example- date format is dd/MM/yyyy Following is sample input

04/11/2019,  11/11/2019, 18/11/2019, 25/11/2019, 02/12/2019, 09/12/2019, 
06/01/2020, 03/02/2020, 10/02/2020, 17/02/2020, 24/02/2020, 02/03/2020, 
09/03/2020, 16/03/2020, 23/03/2020, 30/03/2020, 06/04/2020, 13/04/2020, 
20/04/2020, 27/04/2020

I need to get least date from each month:

output be like-

04/11/2019, 02/12/2019, 06/01/2020, 03/02/2020, 02/03/2020, 06/04/2020

Can anyone help?

like image 391
DevSay Avatar asked Nov 14 '19 07:11

DevSay


People also ask

How do I find the last day of the month in Excel?

The Excel EOMONTH function returns the last day of the month, n months in the past or future. You can use EDATE to calculate expiration dates, due dates, and other dates that need to land on the last day of a month.

How do you find the smallest earliest date in Excel?

Explaining formula in cell B8 The MIN function returns the smallest earliest date from cell range $B$3:$C$5. The dollar signs make sure that the cell reference doesn't change when we copy the cell and paste it to the cells below.

How do you find the latest date on a date range?

If you want to find out the latest dates in the range, you can enter the formula =MAX(A1:D7), and press the Enter key. This formula of =LARGE(A1:D7,1) will help you get the latest dates quickly.

How do you calculate a date in Excel with a date?

To solve this formula, Excel first extracts the year, month, and day values from the date in B6, then adds 1 to the month value. Next, a new date is assembled by the DATE function, using the same day and year, and month + 1 for month. = DATE(YEAR(B6),MONTH(B6) + 1,DAY(B6)) = DATE(2010,1 + 1,15) = DATE(2010,2,15) = 2 / 15 / 2010


4 Answers

If you are using Java-8 you can use :

List<LocalDate> collect = Stream.of(strings)
        .map(s -> LocalDate.parse(s, format)) // convert your strings to LocalDate
        .collect(Collectors.groupingBy(YearMonth::from)) // group by year and month
        .values().stream()
        .map(a -> a.stream().sorted().limit(1).findFirst().get())
        .sorted()
        .collect(Collectors.toList()); // collect the results

outputs

[2019-11-04, 2019-12-02, 2020-01-06, 2020-02-03, 2020-03-02, 2020-04-06]

Ideone demo

like image 189
YCF_L Avatar answered Nov 08 '22 18:11

YCF_L


public List<LocalDate> filterDates(List<LocalDate> dates) {
    return dates.stream()
            .collect(Collectors.groupingBy(YearMonth::from))
            .values()
            .stream()
            .map(Collections::min) // .map(Collections::max)
            .collect(Collectors.toList());
}

Instead of .map(Collections::min) you can use the following:

.map(list -> {
    list.sort(Comparator.naturalOrder());
    return list.get(0);
})

Or:

.map(list -> list.stream().max(Comparator.naturalOrder()))
like image 21
ruslanys Avatar answered Nov 08 '22 19:11

ruslanys


There’s a wealth of good answers already. On top of those I would like to add:

  1. Don’t put strings in your original list. Put LocalDate objects. If you get string input, parse it before adding to the list.
  2. You don’t need to sort the inner list to get its minimum (or maximum).

In Java 10 or later:

    List<LocalDate> input = List.of(LocalDate.of(2019, 11, 4), 
            LocalDate.of(2019, 11, 11), LocalDate.of(2019, 11, 18),
            LocalDate.of(2019, 11, 25), LocalDate.of(2019, 12, 2),
            LocalDate.of(2019, 12, 9), LocalDate.of(2020, 1, 6),
            LocalDate.of(2020, 2, 3), LocalDate.of(2020, 2, 10),
            LocalDate.of(2020, 2, 17), LocalDate.of(2020, 2, 24),
            LocalDate.of(2020, 3, 2), LocalDate.of(2020, 3, 9),
            LocalDate.of(2020, 3, 16), LocalDate.of(2020, 3, 23),
            LocalDate.of(2020, 3, 30), LocalDate.of(2020, 4, 6),
            LocalDate.of(2020, 4, 13), LocalDate.of(2020, 4, 20),
            LocalDate.of(2020, 4, 27));

    List<LocalDate> minDatePerMonth = input.stream()
            .collect(Collectors.groupingBy(YearMonth::from, Collectors.minBy(Comparator.naturalOrder())))
            .values()
            .stream()
            .map(Optional::orElseThrow)
            .sorted()
            .collect(Collectors.toList());

I am using the overloaded version of Collectors.groupingBy that accepts a downstream collector. groupingBy produces a map, but instead of the map values being lists they are produced by the downstream collector. In this case Collectors.minBy, which produces Optional<LocalDate>. And since the groupingBy would not create a map entry without at least one date, we know that the Optional cannot be empty.

Of course we want nicely formatted output for our user. We produce that in this way:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/uuuu");
    String output = minDatePerMonth.stream()
            .map(formatter::format)
            .collect(Collectors.joining(", "));
    System.out.println(output);

04/11/2019, 02/12/2019, 06/01/2020, 03/02/2020, 02/03/2020, 06/04/2020

In Java 9

The no-arg Optional.orElseThrow was introduced in Java 10. In Java 8 and 9 you may use for example:

            .map(old -> old.orElseThrow(() -> new IllegalStateException("This can’t happen")))

In Java 8

List.of was introduced in Java 9. However you said that you had already got the list, so I consider how to initialize it outside the question. I trust readers to find their way of initializing it in Java 8 if required.

like image 42
Ole V.V. Avatar answered Nov 08 '22 19:11

Ole V.V.


Here is a sequence that you will need to do

  1. Parse your Strings to LocalDate
  2. Build a map where your key would be a month and your key would be a sorted List.
    1. Go through all the Lists and take the first value from each one

This is your algorithm. I will leave the implementation for you.

like image 43
Michael Gantman Avatar answered Nov 08 '22 18:11

Michael Gantman