Skip to content
Advertisement

Using Stream API to count stats

I have following model representing a Snack bar SnackBarStat – This has a start date for a booking, number of customers in booking and number of Days(customer can set a start date and say for how many days they want to have table)

public class SnackBarStat {
  LocalDate startDate;
  int numberOfDays;
  int customers;
}

Now given a list of such stats I am trying to find for each date how many customer are there in snack bar

For example if input is

Start date 13-9-2021, Customers: 2 , numberOfDays: 3
Start date 12-9-2021, Customers: 3 , numberOfDays: 2
Start date 13-9-2021, Customers: 1 , numberOfDays: 1

Expected output is

{2021-09-12=3, 2021-09-13=6, 2021-09-14=2, 2021-09-15=2}

What I have tried so far. I have created a simple logic to iterate over each startDate, expand the dates based on numberOfDays and then for each date add them to a map with summing customers on that date

public class SnackBarOccupanceTest {
  public static void main(String args[]){
    SnackBarStat snackBarStat1 = new SnackBarStat();
    snackBarStat1.setStartDate(LocalDate.of(2021, 9, 13));
    snackBarStat1.setCustomers(2);
    snackBarStat1.setNumberOfDays(3);

    SnackBarStat snackBarStat2 = new SnackBarStat();
    snackBarStat2.setStartDate(LocalDate.of(2021, 9, 12));
    snackBarStat2.setCustomers(3);
    snackBarStat2.setNumberOfDays(2);

    SnackBarStat snackBarStat3 = new SnackBarStat();
    snackBarStat3.setStartDate(LocalDate.of(2021, 9, 13));
    snackBarStat3.setCustomers(1);
    snackBarStat3.setNumberOfDays(1);

    Map<LocalDate, Integer> occupancePerDate = new HashMap<>();
    List<SnackBarStat> snackBarStats = List.of(snackBarStat1, snackBarStat2, snackBarStat3);
    for(SnackBarStat snackBarStat:snackBarStats){
      var expandedDates = expandDates(snackBarStat.getStartDate(), snackBarStat.getNumberOfDays());
      for(LocalDate eachDate : expandedDates){
        if(occupancePerDate.get(eachDate) != null){
          var existingCustomers = occupancePerDate.get(eachDate);
          occupancePerDate.put(eachDate, existingCustomers + snackBarStat.getCustomers());
        }else{
          occupancePerDate.put(eachDate, snackBarStat.getCustomers());
        }
      }
    }
    System.out.println(occupancePerDate);
  }

  public static List<LocalDate> expandDates (LocalDate startDate, int numberOfDays){
    List<LocalDate> dateRanges = new ArrayList<>();
    for(int i = 0; i < numberOfDays; i++){
      dateRanges.add(startDate.plusDays(i));
    }
    return dateRanges;
  }
}

This code works. I was wondering if there was shorter way with stream api to do the same. Note the code is just for demo purpose and I have made use of better code practice in actual code.

Advertisement

Answer

You can use flatMap to “expand” the dates, and then you can use groupingBy to group the expanded dates:

Map<LocalDate, Integer> occupancePerDate = snackBarStats.stream()
    .flatMap(x -> // expand the dates
        IntStream.range(0, x.getNumberOfDays())
            // for each date, we create a new SnackBarStat with 
            // that date, numberOfDays=1, and the same number of customers
            .mapToObj(n -> new SnackBarStat(x.getStartDate().plusDays(n), 1, x.getCustomers()))
    ).collect(Collectors.groupingBy(
        SnackBarStat::getStartDate, // group by start date
        Collectors.summingInt(SnackBarStat::getCustomers) // for each group, sum the customers
    ));

Though this is shorter, note that it is probably slower than your loops.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement