java - 使用 startDate 和 endDate 按月分组

标签 java java-stream grouping

我有一个类模块,其中包含

private LocalDate startDate;        
private LocalDate endDate;       
private Map<Teacher, Integer> workedHoursPerDay;

每个老师都有一个属性

private int hourlyWage;   

请求是获取按月分组的所有教师的每月预算。预期返回的是Map。我进行了搜索,但我不知道如何使用 startDate 和 endDate 来做到这一点。

单个模块的每月支出是分配给该模块的所有教师在该月所有工作日内的累计成本。

逻辑是: -从所有模块中获取(Set)教授该模块的老师和小时数(MapworkingHoursPerDay)。然后将每位教师的工作时间乘以小时工资。最后,使用每个模块的 startDate 和 endDate 按月对成本进行分组。

我已经做了类似的事情,但没有得到预期的结果

公共(public) map calculateCumulativeMonthlySpends() {

    //Get the min startDate of all the  modules
    LocalDate localDateStart = modules.stream()
            .flatMap(modules -> module.getWorkingDays().stream())
            .min(LocalDate::compareTo).get();
    //Get the max endDate of all the modules
    LocalDate localDateEnd = modules.stream()
            .flatMap(module -> module.getWorkingDays().stream())
            .max(LocalDate::compareTo).get();

    //Iterate from the start date to the end date, calculate the daily cost for all the module
    //and sum the cost of all the days for the all modules
    //Then create a map with moth as a key and the cost of all the modules for each month
    //Finally sort the map by key

    return Stream.iterate(localDateStart, d -> d.plusDays(1))
            .limit(ChronoUnit.DAYS.between(localDateStart, localDateEnd) + 1)
            .collect(Collectors.toMap(LocalDate::getMonth, day -> dailyCost(day, modules), Integer::sum))
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));
}

/**
 * Calculate the daily cost for all the modules
 * Check if the modules's working days contain the attribute day
 * If yes sum(),  add the daily cost of this module to the other modules' daily cost
 *
 * @param day
 * @param modules
 * @return
 */
private Integer dailyCost(LocalDate day, Set<Module> modules) {

    return modules.stream()
            .filter(module -> module.getWorkingDays().contains(day))
            .flatMap(p -> p.getCommittedHoursPerDay().entrySet().stream())
            .mapToInt(e -> e.getKey().getHourlyWage() * e.getValue())
            .sum();
}

代码已修改。我现在已经得到了预期的结果,但我希望有更好的方法来做到这一点。

预计:{1月=19529、2月=26909、3月=35593、4月=60243、5月=79172、6月=70135、7月=72470、8月=40161、9月=21750、10月=8636、 十一月=1344}

最佳答案

如果没有更多信息,很难找到代码中的错误。但解释一下如何自己找到它可能会对您更有帮助。

dailyCost 中的单个表达式太复杂,无法调试。 Java 流非常棒,但它们有一个非常大的问题:使用交互式调试器调试它们非常困难(请参阅 Problems using interactive debuggers with Java 8 streams 不幸的是没有有用的答案)。

秘诀是让你的运算符(operator)链简短而明显。在可能的情况下分成代表单一职责并且可以单独进行单元测试的单独方法。然后您就可以放心地将这些方法构建到您的解决方案中。

例如:

.filter(p -> (p.getStartDate().isBefore( day)  || p.getStartDate().equals(day)))
.filter(p -> p.getEndDate().isAfter(day) || p.getEndDate().equals(day))

可能成为:

.filter(p -> p.wasActiveOnDay(day))

还有:

.mapToInt(p -> p.getCommittedHoursPerDay().entrySet().stream()
    .mapToInt(e -> e.getKey().getHourlyWage() * e.getValue()).sum())

可能成为:

.mapToInt(Module:getTotalWages)

其中每一个都可以作为模块测试的一部分进行单元测试。这会给你留下:

return modules.stream()
    .filter(m -> m.wasActiveOnDay(day))
    .mapToInt(Module::getTotalWages).sum();

该语句更容易调试。

关于java - 使用 startDate 和 endDate 按月分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59079848/

相关文章:

c# - 多个 linq "from"和变量可见性

替换两个给定时间之间的列中的值

python - 按日期排序事件

java - Clojure 性能优化与等效 Java

java - Debian 启停守护进程。 Java启动jar文件

Java 流独立变量的最小值/最大值

Java 8 流 : convert comma separated string to the list of abstract Enum

java - TextView 和缩放

java - 如何获取运行桌面应用程序的 JAR 的名称?

arrays - 使用流查找两个相似的数组