在 Scala 中将 Futures 列表合并到 Map 中

Consolidate a list of Futures into a Map in Scala

我有两个案例 类 P(id: String, ...)Q(id: String, ...),以及两个返回期货的函数:

  1. 一个在给定 id-s 列表的情况下检索对象列表的方法: def retrieve(ids: Seq[String]): Future[Seq[P]] = Future { ... }

    如果没有找到所有 id-s,结果的长度可能比输入的短。

  2. 进一步将 P 转换为其他类型的 Qdef transform(p: P): Future[Q] = Future { ... }

我最后想要的是,如下。给定ids: Seq[String],计算一个Future[Map[String, Option[Q]]].

ids 中的每个 id 都应该是映射中的键,成功检索时带有 id -> Some(q)(即出现在 retrieve 的结果中)并且也转型成功。否则,地图应包含 id -> NoneEmpty.

我怎样才能做到这一点?

PQ 上有 .id 属性 吗?您将需要一个来创建地图。是这样的吗?

for {
  ps <- retrieve(ids)
  qs <- Future.sequence(ps.map(p => transform(p))
} yield ids.map(id => id -> qs.find(_.id == id)).toMap

请记住,Map[String,Option[X]] 通常不是必需的,因为如果您有 Map[String,X],地图上的 .get 方法将为您提供 Option[X]

编辑:现在假定 P 有一个等于原始 id-String 的成员 id,否则 id 之间的连接ps 在 retrieve.

之后丢失
def consolidatedMap(ids: Seq[String]): Future[Map[String, Option[Q]]] = {
  for {
    ps <- retrieve(ids)
    qOpts <- Future.traverse(ps){
      p => transform(p).map(Option(_)).recover {
        // TODO: don't sweep `Throwable` under the 
        // rug in your real code
        case t: Throwable => None 
      }
    }
  } yield {
    val qMap = (ps.map(_.id) zip qOpts).toMap
    ids.map{ id => (id, qMap.getOrElse(id, None)) }.toMap
  }
}

从检索到的 P 和转换后的 Q 构建一个中间 Map,因此 ids-to-q-Options 映射的构建可以在线性时间。