Search code examples
scalascala-collectionsscala-cats

Scala how to call a future function on hashmap items


I have a hashmap map: Map[A, Seq[B]] and I want to call a future function (that returns Future[Either[Error, Unit]]) for each B in my map.

For example, given the following function def fooFunc(hashMap: Map[A, Seq[B]]): Future[Either[Error, Unit]]

I tried something like

def fooFunc(hashMap: Map[A, Seq[B]]): Future[Either[Error, Unit]] = {
  val result = for {
   _ <- hashMap.map(entry => 
          entry._2.map( value => 
            Future.sequence(futureFunc(value, entry._1)).liftF
          )
        )
   } yield ()
  result.value
}

which gives the compile error Type mismatch, expected: NotInferedM[Future[NotInferedA]], actual: [Future[Either[Error, Unit]]]

This is my first time dealing with futures and iterating over hashmap, so I'm confused how to exactly go about this and deal with the Iterable. Any tips would be appreciated


Solution

  • We can map over a HashMap just like any other collection such as List

    m.flatMap({ case (key, values) => values.map(f(_, key)) })
    

    where m: Map[A, Seq[B]] and f: (B, A) => Future[Unit]. This returns Iterable[Future[Unit]] so we can use Future.sequence to invert to Future[Iterable[Unit]]

    m
      .flatMap({ case (key, values) => values.map(f(_, key)) })
      .pipe(Future.sequence(_))
    

    Futures internally represent success/failure using Try which we can convert to Either using transform and toEither like so

    m                                                           // : Map[A, Seq[B]]
      .flatMap({ case (key, values) => values.map(f(_, key)) }) // : Iterable[Future[Unit]]
      .pipe(Future.sequence(_))                                 // : Future[Iterable[Unit]]
      .map(_ => ())                                             // : Future[Unit]
      .transform(tryResult => Success(tryResult.toEither))      // : Future[Either[Throwable, Unit]]
    

    which returns the required Future[Either[Error, Unit]] type. The pipe method comes from import util.chaining._

    Personally, the transformation from Map[A, Seq[B]] to Future[Either[Error, Unit]], feels a bit suspect.