Home > Enterprise >  How to replace a while loop for a stream or a Flux to iterate my elements
How to replace a while loop for a stream or a Flux to iterate my elements

Time:02-16

I got this code

public String calculateShippingEstimateDate(LocalDateTime initialDate, Integer totalDaysToAdd, SaveOrderSourceData saveOrderSourceData) {

        int workDays = totalDaysToAdd;
        LocalDateTime finalDate = LocalDateTime.of(initialDate.getYear(),
                initialDate.getMonth(),
                initialDate.getDayOfMonth(),
                initialDate.getHour(),
                initialDate.getMinute(),
                initialDate.getSecond());

        while (workDays > 0) {
            finalDate = finalDate.plusDays(1);

            if (!(
                    DateUtility.isWeekend(finalDate.getDayOfWeek())
                            || checkHolidays(saveOrderSourceData, finalDate)
            )) {
                workDays--;
            }
        }
        return finalDate.toString();
    }



    private boolean checkHolidays(SaveOrderSourceData saveOrderSourceData, LocalDateTime finalDate) {
        return saveOrderSourceData.getHolidays()
                .stream().anyMatch(holiday -> getHoliday(holiday).isEqual(finalDate.toLocalDate()));
    }

    private LocalDate getHoliday(Holiday holiday){
        return LocalDate.of(holiday.getYear(),holiday.getMonth(), holiday.getDay());
    }

i'm going to explain this code.

It's important to know that the estimateDate for this example is equals to five days after the initialDate.

We have an initialDate(15/02/2022) that is equals to the finalDate.

We enter into the while loop, it validates if workdays (in this iteration is 5) is mayor to 0, it's false, so finalDate increases its value, and it validates if the date(16/02/2022) is a weekend or a holiday, and so on, when finalDate is equals to (19/02/2022) it doens't decrease the value of workDays. Finally when the finalDate is (21/02/2022) workDays is equals to 0, and finalDate is a Monday, so the while loop ends.

Holiday Class

import lombok.Builder;
import lombok.Data;

@Data
@Builder(toBuilder = true)
public class Holiday {
    private final String id;
    private final int year;
    private final int month;
    private final int day;
}

DateUtility Class

import java.time.DayOfWeek;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import static java.time.DayOfWeek.SATURDAY;
import static java.time.DayOfWeek.SUNDAY;

public class DateUtility {

    private DateUtility() {
    }

    public static boolean isWeekend(DayOfWeek dayOfWeek) {
        return SATURDAY.equals(dayOfWeek)
                || SUNDAY.equals(dayOfWeek);
    }
}

SaveOrderSourceData class

@Data
@Builder(toBuilder = true)
public class SaveOrderSourceData {

    private  final List<Holiday> holidays;
}

CodePudding user response:

That will be an equivalent of your while loop.

  • iterate() takes initialDate.plusDays(1) as a seed, and spawns new element accordingly to the UnaryOperator date -> date.plusDays(1);
  • filter() - lives only working days in the stream (with assumption that DateUtility and checkHolidays() do there job correctly);
  • limit() - as it's name suggests limit the number of elements in the stream to a given value (note, limit() is applied when weekends or holidays are filtered out);
  • max() - produces the optional result, optional might be empty initialDate and all other days the given range are weekends or holidays and the stream will be empty in this case;
  • map() - is applied on the optional if the value is present;
  • orElse() - provides an alternative value (as well as in your implementation it'll return the initialDate if no further date was found).
    public String calculateShippingEstimateDate(LocalDateTime initialDate, 
                                                Integer totalDaysToAdd, 
                                                SaveOrderSourceData saveOrderSourceData) {

        return Stream.iterate(initialDate.plusDays(1), date -> date.plusDays(1))
                .filter(date -> !(DateUtility.isWeekend(date.getDayOfWeek())
                                || checkHolidays(saveOrderSourceData, date)))
                .limit(totalDaysToAdd)
                .max(Comparator.naturalOrder())
                .map(LocalDateTime::toString)
                .orElse(initialDate.toString());
    }

I've tested this solution with your utility classes it yields the same result as your imperative implementation

    public static void main(String[] args) {
        System.out.println(new LDTUtil().getShippingDateLoop(LocalDateTime.now(), 5, SaveOrderSourceData.builder().holidays(List.<Holiday>of()).build()));
        System.out.println(new LDTUtil().getShippingDateLoop(LocalDateTime.now(), 5, SaveOrderSourceData.builder().holidays(List.<Holiday>of()).build())   "\n");

        System.out.println(new LDTUtil().getShippingDateLoop(LocalDateTime.now(), 27, SaveOrderSourceData.builder().holidays(List.of(Holiday.builder().id("id1").year(2022).month(3).day(3).build())).build()));
        System.out.println(new LDTUtil().getShippingDateLoop(LocalDateTime.now(), 27, SaveOrderSourceData.builder().holidays(List.of(Holiday.builder().id("id1").year(2022).month(3).day(3).build())).build()));
    }

output

2022-02-22T21:43:25
2022-02-22T21:43:25

2022-03-25T21:43:25
2022-03-25T21:43:25
  • Related