使用一个服务调用(List)的结果来获取另一个列表

时间:2018-06-13 10:05:30

标签: scala

我有一个服务,它返回一个数据类型(下面为Foo),其中包含一个id列表,用于下面的第二个服务调用getBar

case class Foo(barIds: List[Int])
case class Bar(id: Int)

val f = Future(List(Foo(List(1, 2)), Foo(List(5, 6))))
def getBar(l: List[Int]) = Future(l.map(Bar(_)))

我需要的是Future[List[Foo,List[Bar]]]

我首先尝试了嵌套的for-comprehension但是

val rr = for {
    foos <- f
} yield  for {
    foo <- foos
    bars <- getBar(foo.barIds) // won't work as this is a Future and foos is a list
} yield (foo,bars)   
然后我玩了一个映射游戏,(闻起来很可怕):

f.map(
    foos => foos.map(foo => (foo, foo.barIds)))
        .map(ts => ts.map(t => (t._1, getBar(t._2)))
)

但这给了我一个Future[List[Foo,Future[List[Bar]]]]

应该有办法获得Future[List[Foo,List[Bar]]]并希望以更清洁的方式获得

这是一个scalafiddle https://scalafiddle.io/sf/P0FRIGs/0

请注意我之后的值是:带Foo的元组和“他们”关联的Bar值列表:

List(
     (Foo(List(1, 2)),List(Bar(1), Bar(2))),
     (Foo(List(5, 6)),List(Bar(5), Bar(6)))
)

4 个答案:

答案 0 :(得分:2)

我不确定你希望你的列表是如何构建的,但是这样的事情能做到这一点吗?

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.Success

case class Foo(barIds: List[Int])
case class Bar(id: Int)

val f: Future[List[Foo]] = Future(List(Foo(List(1, 2)), Foo(List(5, 6))))
def getBar(l: List[Int]): Future[List[Bar]] = Future(l.map(Bar(_)))

val bars: Future[List[Bar]] = f.flatMap(x => getBar(x.flatMap(_.barIds)))

val out: Future[List[(Foo, List[Bar])]] = for {
  foo <- f
  bar <- bars
} yield {
  foo.map(x => (x, bar))
}

out andThen {case Success(v)=>println(v)  }

// List((Foo(List(1, 2)),List(Bar(1), Bar(2), Bar(5), Bar(6))), (Foo(List(5, 6)),List(Bar(1), Bar(2), Bar(5), Bar(6))))

[Fiddle]

答案 1 :(得分:2)

我会围绕getBar创建一个帮助程序包装器方法,它返回传入的foo,并将其与Future.traverse结合使用,如下所示:

private def getFooAndBars(foo: Foo): Future[(Foo, List[Bar])] =
  getBar(foo.barIds).map(foo -> _)

val res: Future[List[(Foo, List[Bar])]] =
  f.flatMap(Future.traverse(_)(getFooAndBars))

Future.traverse会占用每个foo,在其上调用getFooAndBars,并展开列表,以便获得Future[List]而不是List[Future]。< / p>

答案 2 :(得分:1)

有时将事物映射到所需的结构化可能会很棘手,在这种情况下,您可以选择标识符,以帮助您以更有意义的方式导航复杂性。你走了。

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

case class Foo(barIds: List[Int])
case class Bar(id: Int)

def getBar(l: List[Int]) = Future(l.map(Bar(_)))

val fooListFuture = Future(List(Foo(List(1, 2)), Foo(List(5, 6))))

// You want to get
//Future[List[(Foo,List[Bar])]]

val yourRequireFuture = fooListFuture.flatMap(fooList => {
  Future.sequence(fooList.map(foo =>
    getBar(foo.barIds).map(barList => (foo, barList))
  ))
})

答案 3 :(得分:1)

使用map的解决方案是正确的,但getBar也会返回未来,您将获得嵌套的未来结果Future[List[Foo,Future[List[Bar]]]]。您必须使用Future.sequence将这些未来合并在一起。

val res: Future[List[(Foo,List[Bar])]] = f.flatMap(foos =>
 Future.sequence(
   foos.map(foo =>
     getBar(foo.barIds).map(foo -> _)
   )
  )
)

此外,您的for循环解决方案也有效,但您必须另外解决未来的价值。

  val res2: Future[List[(Foo,List[Bar])]] = for {
    foos <- f
    foo <- foos
    fbars <- getBar(foo.barIds) // won't work as this is a Future and foos is a list
    bars <- fbars //because fbars is future, you have to resolve its value. bars is resolved future value. 
  } yield (foo,bars)