web-dev-qa-db-ja.com

Scala Option [Future [T]] to Future [Option [T]]

ScalaでOption[Future[T]]Future[Option[T]]に変換するにはどうすればよいですか?

私はそれを使用したい:

val customerAddresses = for {
  a <- addressDAO.insert(ca.address) // Future[Address]
  ia <- ca.invoiceAddress.map(addressDAO.insert) // Option[Future[Address]]
} yield (a, ia) // Invalid value have to be two futures

ここに署名挿入方法

def insert(address: Address): Future[Address]

caはCustomerDataです

case class CustomerData(address: Address, invoiceAddress: Option[Address])
22
Michał Jurczuk
import scala.concurrent.Future
import scala.concurrent.ExecutionContext

def f[A](x: Option[Future[A]])(implicit ec: ExecutionContext): Future[Option[A]] = 
  x match {
     case Some(f) => f.map(Some(_))
     case None    => Future.successful(None)
  }

例:

scala> f[Int](Some(Future.successful(42)))
res3: scala.concurrent.Future[Option[Int]] = Success(Some(42))

scala> f[Int](None)
res4: scala.concurrent.Future[Option[Int]] = scala.concurrent.impl.Promise$KeptPromise@c88a337
26
Kevin Meredith

標準ライブラリには、オプションでFuture.sequenceを使用するメソッドが用意されていますが、残念ながらそれらを一緒に組み込む必要があります。

簡単な方法として:

_def swap[M](x: Option[Future[M]]): Future[Option[M]] =
    Future.sequence(Option.option2Iterable(x)).map(_.headOption)
_

注:暗黙の_Option.option2Iterable_が既にスコープに入っていることがわかりました。そのため、コードをFuture.sequence(x).map(_.headOption)に減らして提供する必要がない場合があります

または、拡張メソッドを好むかもしれません:

_implicit class OptionSwitch[A](f: Option[Future[A]]) {
    import scala.concurrent.Future

    def switch: Future[Option[A]] = Future.sequence(Option.option2Iterable(f))
      .map(_.headOption)
  }


val myOpt = Option(Future(3))
myOpt.switch
_
8
RawToast

別のソリューションを次に示します。

def swap[T](o: Option[Future[T]]): Future[Option[T]] =
  o.map(_.map(Some(_))).getOrElse(Future.successful(None))

トリックは、Option[Future[T]]Option[Future[Option[T]]]に変換することです。これは簡単で、そのOptionから値を抽出します。

3
Yaroslav

アプリケーションの依存関係として猫がいる場合、最も美しい方法はtraverseを使用することです

import cats._
import cats.implicits._

val customerAddresses = for {
  a <- addressDAO.insert(ca.address) // Future[Address]
  ia <- ca.invoiceAddress.traverse(addressDAO.insert) // Future[Option[Address]]
} yield (a, ia)
2
gun

フューチャーのリスト(または任意のTraversableOnce)があり、リスト全体を計算するために単一のフューチャーが必要な場合は、Future.sequenceまたはFuture.traverseを使用します。 Optionは1または0要素のリストのように考えることができますが、技術的にはリストではないため、この場合は少し変換する必要があります。とにかく、これは通常それを行うコードです:

  val optionFuture:Option[Future[String]] = ???

  val futureOption:Future[Option[String]] = Future.sequence(optionFuture.toIterable).map(_.headOption)

あなたの例では、より良いFuture.traverseを使用します:

  val customerAddresses = for {
    a <- addressDAO.insert(ca.address) // Future[Address]
    ia <- Future.traverse(ca.invoiceAddress.toIterable)(addressDAO.insert).map(_.headOption) // Future[Option[Address]]
  } yield CustomerData(a, ia) // Types OK
1
Guillem I
val customerAddresses = for {
  a <- addressDAO.insert(ca.address) // Future[Address]
  ia <- ca.invoiceAddress.map(x => addressDAO.insert(x).map(_.map(k => Some(k))).getOrElse(Future.successful(None)))
} yield (a, ia)
0