如何在TimeFold约束流中遍历数组并计算分数?
问题:在OptaPlanner约束中遍历StandardWorkAmounts计算分数
定义的Java类
DaySetting类
public class DaySetting { String businessDate; Integer weekNo; Integer monthNo; ArrayList<StandardWorkAmount> standardWorkAmounts; }
StandardWorkAmount类
public class StandardWorkAmount { String jobType; Integer workMinutes; public String getJobType() { return jobType; } public void setJobType(String jobType) { this.jobType = jobType; } public Integer getWorkMinutes() { return this.workMinutes; } public void setWorkMinutes(Integer workMinutes) { this.workMinutes = workMinutes; } }
待完善的Constraint代码
用户已编写的部分代码如下:
Constraint daySettingStandardNumberOfHours(ConstraintFactory constraintFactory) { return constraintFactory.forEach(DaySetting.class) .filter((daySetting)->daySetting.getStandardWorkAmounts()!= null) // Iterate through daySetting.getStandardWorkAmounts and find the optimal choice. .penalize(HardMediumSoftScore.ONE_SOFT, .asConstraint("Day Settings Standard Number Hours"); }
实现方案
根据需求,分两种常见场景给出实现:
场景1:针对单个StandardWorkAmount元素计算分数
如果需要对每个StandardWorkAmount单独校验(比如特定工种的工作时长不符合要求就扣分),可以用flatMap展开集合,再进行过滤和扣分:
Constraint daySettingStandardNumberOfHours(ConstraintFactory constraintFactory) { return constraintFactory.forEach(DaySetting.class) // 过滤掉standardWorkAmounts为空的情况,避免空指针 .filter(daySetting -> daySetting.getStandardWorkAmounts() != null && !daySetting.getStandardWorkAmounts().isEmpty()) // 将每个DaySetting展开为其包含的StandardWorkAmount元素 .flatMap(daySetting -> daySetting.getStandardWorkAmounts().stream()) // 示例:筛选出工作时长小于120分钟的记录(可根据实际需求调整条件) .filter(workAmount -> workAmount.getWorkMinutes() < 120) // 每条不符合条件的记录扣1个软分数 .penalize(HardMediumSoftScore.ONE_SOFT) .asConstraint("Day Settings Standard Number Hours"); }
场景2:针对DaySetting的总工作时长计算分数
如果需要基于DaySetting下所有StandardWorkAmount的总时长与标准值对比扣分,直接在penalize的分数计算逻辑中处理:
Constraint daySettingStandardNumberOfHours(ConstraintFactory constraintFactory) { // 定义标准日工作时长(示例为8小时=480分钟) final int STANDARD_DAILY_MINUTES = 480; return constraintFactory.forEach(DaySetting.class) .filter(daySetting -> daySetting.getStandardWorkAmounts() != null && !daySetting.getStandardWorkAmounts().isEmpty()) .penalize(HardMediumSoftScore.ONE_SOFT, // 计算总时长与标准值的差值绝对值,以此作为扣分数量 daySetting -> { int totalWorkMinutes = daySetting.getStandardWorkAmounts().stream() .mapToInt(StandardWorkAmount::getWorkMinutes) .sum(); return Math.abs(totalWorkMinutes - STANDARD_DAILY_MINUTES); }) .asConstraint("Day Settings Standard Number Hours"); }
关键说明
- 必须在
filter中增加!daySetting.getStandardWorkAmounts().isEmpty()判断,避免空集合导致的计算异常 flatMap用于将集合元素展开,适合处理单个元素的校验逻辑penalize的第二个参数是分数计算函数,可根据实际业务需求调整(比如固定扣分、按差值比例扣分等)
内容的提问来源于stack exchange,提问作者I. Ahmed
相关产品推荐
相关产品推荐

