Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert Map[A,Future[B]] to Future[Map[A,B]]?

Tags:

scala

akka

future

I've been working with the Scala Akka library and have come across a bit of a problem. As the title says, I need to convert Map[A, Future[B]] to Future[Map[A,B]]. I know that one can use Future.sequence for Iterables like Lists, but that doesn't work in this case.

I was wondering: is there a clean way in Scala to make this conversion?

like image 217
Chris Grimm Avatar asked Jul 04 '13 23:07

Chris Grimm


Video Answer


1 Answers

See if this works for you:

val map = Map("a" -> future{1}, "b" -> future{2}, "c" -> future{3})     val fut = Future.sequence(map.map(entry => entry._2.map(i => (entry._1, i)))).map(_.toMap) 

The idea is to map the map to an Iterable for a Tuple of the key of the map and the result of the future tied to that key. From there you can sequence that Iterable and then once you have the aggregate Future, map it and convert that Iterable of Tuples to a map via toMap.

Now, an alternative to this approach is to try and do something similar to what the sequence function is doing, with a couple of tweaks. You could write a sequenceMap function like so:

def sequenceMap[A, B](in: Map[B, Future[A]])(implicit executor: ExecutionContext): Future[Map[B, A]] = {   val mb = new MapBuilder[B,A, Map[B,A]](Map())   in.foldLeft(Promise.successful(mb).future) {     (fr, fa) => for (r <- fr; a <- fa._2.asInstanceOf[Future[A]]) yield (r += ((fa._1, a)))   } map (_.result) } 

And then use it in an example like this:

val map = Map("a" -> future{1}, "b" -> future{2}, "c" -> future{3})     val fut = sequenceMap(map) fut onComplete{   case Success(m) => println(m)   case Failure(ex) => ex.printStackTrace() } 

This might be slightly more efficient than the first example as it creates less intermediate collections and has less hits to the ExecutionContext.

like image 124
cmbaxter Avatar answered Oct 13 '22 00:10

cmbaxter