Scala合并Case Class列表生成差值对比类及groupBy顺序疑问
Scala 合并两个ScoreOutput列表生成ScoreComparison的最优方案
最优实现方式
根据你的需求,分两种场景给出最优实现:
场景1:两个列表的ID完全匹配且顺序一致
如果能确保originalScores和currentScores中的元素按ID一一对应且顺序相同,直接使用zip方法是最高效的:
// 先修正原代码的ID类型(case class定义id为String,字面量需加引号) case class ScoreOutput(id: String, runDate: String, score: Double) val originalScores: List[ScoreOutput] = List( ScoreOutput("1", "09-21", 5.0), ScoreOutput("2", "09-21", 3.0), ScoreOutput("3", "09-21", 2.0) ) val currentScores: List[ScoreOutput] = List( ScoreOutput("1", "10-01", 20.0), ScoreOutput("2", "10-01", 1.0), ScoreOutput("3", "10-01", 18.0) ) case class ScoreComparison(name: String, originalDate: String, currentDate: String, scoreChange: Double) val scoreDiff: List[ScoreComparison] = originalScores.zip(currentScores).map { case (orig, curr) => ScoreComparison( orig.id, orig.runDate, curr.runDate, curr.score - orig.score ) }
场景2:列表可能存在ID缺失或顺序不一致
如果无法保证ID的匹配性和顺序,将列表转为以ID为键的Map再合并更可靠:
// 转换为Map便于按ID查找 val originalMap = originalScores.map(s => s.id -> s).toMap val currentMap = currentScores.map(s => s.id -> s).toMap // 仅保留两个列表都存在的ID生成结果 val scoreDiff: List[ScoreComparison] = originalMap.keys.intersect(currentMap.keys).toList.map { id => val orig = originalMap(id) val curr = currentMap(id) ScoreComparison(id, orig.runDate, curr.runDate, curr.score - orig.score) } // 若需要包含仅在单个列表中存在的ID,可扩展为: // val allIds = originalMap.keys ++ currentMap.keys // allIds.map { id => // val orig = originalMap.getOrElse(id, ScoreOutput(id, "", 0.0)) // val curr = currentMap.getOrElse(id, ScoreOutput(id, "", 0.0)) // ScoreComparison(id, orig.runDate, curr.runDate, curr.score - orig.score) // }.toList
关于groupBy的顺序疑问
当你通过originalScores ++ currentScores合并列表后执行groupBy(_.id),无法保证组内元素的顺序:
- Scala的
groupBy默认返回普通HashMap,其遍历顺序是不确定的(Scala 2.13之前完全无序,2.13之后虽有有序Map实现,但groupBy仍默认返回无序HashMap)。 - 即使合并后的列表是先原列表元素、后当前列表元素,groupBy生成的List中元素的顺序也依赖于Map的内部实现,不能保证第一个元素一定来自
originalScores。因此这种方式不可靠,不推荐用来区分两个来源的元素。
内容的提问来源于stack exchange,提问作者jbogart
相关产品推荐
相关产品推荐

