1
\$\begingroup\$

I have a List of Objects containing following structure

public class DaySpan{
 private LocalDate date;
 private Integer numberOfDays;
..
..
//getters & setters
}

Lets say the list contains 3 instances of above classes with data (date is in dd/mm/yyyy)

  • 04/02/2020 3
  • 03/02/2020 5
  • 01/02/2020 1

Then I need ouput for count of each day that is getting overlapped in number of days For example the first record will translate to 04/02/2020, 05/02/2020, 06/02/2020 Second record will translate to 03/02/2020, 04/02/2020, 05/02/2020, 06/02/2020, 07/02/2019 and the final record will translate to 01/02/2020 So the output will be

  • 01/02/2020 - 1
  • 03/02/2020 - 1
  • 04/02/2020 - 2
  • 05/02/2020 - 2
  • 06/02/2020 - 2
  • 07/02/2020 - 1

The code I have written is

   public void getDayCount(List<DaySpan> daySpans){

    Map<Object, Long> days = new HashMap();
    days = daySpans.stream()
            .map(this::getDays)
            .collect(
                Collector.of(ArrayList::new, 
                    List::addAll,(left, right) -> { left.addAll(right); return left; }
                    )
                )
            .stream()
            .collect(
                Collectors.groupingBy(Function.identity(), Collectors.counting()));

    days.entrySet().forEach(System.out::println);
  }


  public List<LocalDate> getDays(DaySpan daySpan) {
    return Stream.iterate(daySpan.getStartDate(), d -> d.plusDays(1)).limit(daySpan.getNumberOfDays())
        .collect(Collectors.toList());
  }

Is there a better way?

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

You came far to a good solution.

public void getDayCount(List<DaySpan> daySpans){
    Map<LocalDate, Long> freqs = daySpans.stream()
            .flatMap(daySpan ->
                IntStream.range(0, daySpan.numberOfDays)
                    .mapToObj(i -> daySpan.date.plusDays(i)))
            .collect(
                Collectors.groupingBy(Function.identity(), Collectors.counting()));

    freqs.entrySet().stream()
            .sorted(Map.Entry::getKey)
            .forEach(System.out::println);
}
  • Creating a Stream<LocalDate> for some DaySpan is simply realized by an IntStream.range.
  • A flatMap makes a larger Stream of LocalDates.
  • Turning them into a counting map you already did.
  • Sorting them on the date remains.
  • You printed the entries as Entry, so I kept it that way.
\$\endgroup\$
1
\$\begingroup\$

Yes, you can use flatMap to convert a stream of streams into a single stream, which achieves your goal of accumulating the results of all the getDays calls into one collection:

public void getDayCount(List<DaySpan> daySpans) {
    Map<Object, Long> days = daySpans.stream()
            .map(this::getDays)
            .flatMap(o -> o.stream())
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

    days.entrySet().forEach(System.out::println);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.