I have a service that returns a datatype (Foo
below) that contains a list of ids to be used for a second service call getBar
below
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(_)))
What i need is Future[List[Foo,List[Bar]]]
I tried first a nested for-comprehension but
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)
I then played a mapping game, (which smells horrible):
f.map(
foos => foos.map(foo => (foo, foo.barIds)))
.map(ts => ts.map(t => (t._1, getBar(t._2)))
)
But that gives me a Future[List[Foo,Future[List[Bar]]]]
There should be way to get Future[List[Foo,List[Bar]]]
and hopefully in a much cleaner way
Here is a scalafiddle https://scalafiddle.io/sf/P0FRIGs/0
Note the value i am after is: tuples with Foo and a list of "their" associated Bar values:
List(
(Foo(List(1, 2)),List(Bar(1), Bar(2))),
(Foo(List(5, 6)),List(Bar(5), Bar(6)))
)
I'd create a helper wrapper method around getBar
that returns the foo
passed in, and combine that with Future.traverse
like so:
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
will take each foo
, call getFooAndBars
on it, and flatten the list so that you get a Future[List]
instead of a List[Future]
.
Mapping things into required structured can be tricky sometimes, in such cases you can choose your identifiers in a way that these help you navigate the complexity in a more meaningful way. Here you go.
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))
))
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With