You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Doobie将SQL结果集转换为Map[ID, List[VALUE]]?

解决方案:将Doobie查询结果转为Map[ID, List[VALUE]]

你的核心需求是把重复ID对应的多个Value聚合为Map[ID, List[VALUE]],原代码的问题在于误用了traverse方法——这个方法用于遍历集合并执行Applicative类型的操作,而你这里只是纯内存中的数据聚合,完全不需要用到它,这也是编译报错的原因((ID, T2)没有默认的Applicative实例)。

正确实现方式

直接利用Scala集合的groupBy和mapValues(或view.mapValues避免中间集合复制)完成聚合:

val query = sql"""select blah""".query[(ID, VALUE)]
val result: ConnectionIO[Map[ID, List[VALUE]]] = 
  query.to[List].map { tuples =>
    // 先按ID分组,得到Map[ID, List[(ID, VALUE)]]
    tuples.groupBy(_._1)
      // 把每个分组里的元组转换为Value的列表
      .view.mapValues(_.map(_._2))
      // 转换为不可变Map(如果不需要严格不可变,也可以省略.toMap)
      .toMap
  }

代码解释

  1. query.to[List]:从数据库获取所有(ID, VALUE)元组的列表,返回类型是ConnectionIO[List[(ID, VALUE)]]。
  2. .map(...):在ConnectionIO的上下文里对结果列表做聚合操作——
    • groupBy(_._1):以元组的第一个元素(ID)为键,把相同ID的元组分到一起。
    • view.mapValues(_.map(_._2)):对每个分组的元组列表,提取出第二个元素(Value)组成新列表;用view可以避免创建中间的Map副本,提升性能。
    • .toMap:将视图转换为标准的不可变Map类型。

备选实现(手动折叠)

如果你想手动控制聚合过程,也可以用foldLeft构建Map:

val result: ConnectionIO[Map[ID, List[VALUE]]] = 
  query.to[List].map { tuples =>
    tuples.foldLeft(Map.empty[ID, List[VALUE]]) { (acc, (id, value)) =>
      acc.updated(id, value :: acc.getOrElse(id, Nil))
    }.view.mapValues(_.reverse).toMap
  }

这种方式需要最后反转列表(因为是头插法),适合需要自定义聚合逻辑的场景。


内容的提问来源于stack exchange,提问作者Paul McKenzie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 10:25:33