Конвертировать Seq [Try [Option (String, Any)]] в Try [Option [Map [String, Any]]] - PullRequest
0 голосов
/ 31 января 2020

Как удобно преобразовать Seq [Try [Option [String, Any]]] в Try [Option [Map [String, Any]]].

Если любой Try перед преобразованием выдает исключение, преобразованный Попробуй брось.

Ответы [ 5 ]

2 голосов
/ 31 января 2020

Если предположить, что тип ввода имеет кортеж внутри Option, то это должно дать вам желаемый результат:

val in: Seq[Try[Option[(String, Any)]]] = ???

val out: Try[Option[Map[String,Any]]] = Try(Some(in.flatMap(_.get).toMap))

Если любой из Try s равен Failure, тогда внешний Try поймает исключение, вызванное get, и вернет Failure

. Some здесь, чтобы дать правильный тип возврата

get извлекает Option из Try (или вызывает исключение)

Использование flatMap вместо map удаляет оболочку Option, сохраняя все значения Some и отбрасывая значения None, давая Seq[(String, Any)]

Вызов toMap преобразует Seq в Map

0 голосов
/ 31 января 2020

Один из способов добиться этого - использовать foldLeft:

// Let's say this is the object you're trying to convert
val seq: Seq[Try[Option[(String, Any)]]] = ???

seq.foldLeft(Try(Option(Map.empty[String, Any]))) {
  case (acc, e) =>
    for {
      accOption  <- acc
      elemOption <- e
    } yield elemOption match {
      case Some(value) => accOption.map(_ + value)
      case None        => accOption
    }
}

. Вы начинаете с пустой карты. Затем вы используете для понимания go через текущую карту и элемент и, наконец, добавляете новый кортеж на карту, если он присутствует.

0 голосов
/ 31 января 2020

Следующие решения основаны на этом ответе до такой степени, что почти делает вопрос дубликатом.

Метод 1: Использование рекурсии

def trySeqToMap1[X,Y](trySeq : Seq[Try[Option[(X, Y)]]]) : Try[Option[Map[X,Y]]] = {

  def helper(it : Iterator[Try[Option[(X,Y)]]], m : Map[X,Y] = Map()) : Try[Option[Map[X,Y]]] = {
    if(it.hasNext) {
      val x = it.next()
      if(x.isFailure)
        Failure(x.failed.get)
      else if(x.get.isDefined)
        helper(it, m + (x.get.get._1-> x.get.get._2))
      else
        helper(it, m)
    } else Success(Some(m))
  }

  helper(trySeq.iterator)
}

Метод 2: прямое сопоставление с шаблоном, если вы можете получить поток или List вместо этого:

  def trySeqToMap2[X,Y](trySeq : LazyList[Try[Option[(X, Y)]]], m : Map[X,Y]= Map.empty[X,Y]) : Try[Option[Map[X,Y]]] =
    trySeq match {
      case Success(Some(h)) #:: tail => trySeqToMap2(tail, m + (h._1 -> h._2))
      case Success(None) #:: tail => tail => trySeqToMap2(tail, m)
      case Failure(f) #:: _ => Failure(f)
      case _ => Success(Some(m))
    }

note : этот ответ ранее использовался другим методом подписи. Он был обновлен в соответствии с подписью, приведенной в вопросе.

0 голосов
/ 31 января 2020

Если вы хотите использовать функциональную библиотеку поддержки, такую ​​как Cats , то есть два трюка, которые могут помочь в этом:

  1. Многие вещи, такие как List и Try имеют значение traversable , что означает, что (если импликации Cats находятся в области видимости), у них есть метод sequence, который может менять два типа, например, преобразование List[Try[T]] в Try[List[T]] (сбой если какой-либо из элементов в списке является ошибочным).

  2. Почти все типы контейнеров поддерживают метод map, который может работать с содержимым контейнера, поэтому, если у вас есть функция от A до B, тогда map может преобразовать Try[A] в Try[B]. (В языке Cats они являются функторами , но контейнерные типы в стандартной библиотеке обычно уже имеют map.)

Cats не напрямую поддерживает Seq, поэтому этот ответ в основном выражается в виде List.

Учитывая эту сигнатуру типа, вы можете итеративно sequence элемент, который вам нужен, в действительности pu sh тип списка вниз на один уровень в цепочке типов, затем map над этим контейнером для работы с его содержимым. Это может выглядеть так:

import cats.implicits._
import scala.util._

def convert(listTryOptionPair: List[Try[Option[(String, Any)]]]): Try[
  Option[Map[String, Any]]
] = {
  val tryListOptionPair = listTryOptionPair.sequence
  tryListOptionPair.map { listOptionPair =>
    val optionListPair = listOptionPair.sequence
    optionListPair.map { listPair =>
      Map.from(listPair)
    }
  }
}

https://scastie.scala-lang.org/xbQ8ZbkoRSCXGDJX0PgJAQ имеет чуть более полный пример.

0 голосов
/ 31 января 2020

Вот что-то не очень чистое, но может помочь вам начать. Предполагается, что Option[(String,Any)] возвращает первое Failure, если таковые имеются на входе Seq, и просто отбрасывает None элементов.

foo. scala

package foo
import scala.util.{Try,Success,Failure}

object foo {
  val x0 = Seq[Try[Option[(String, Any)]]]()
  val x1 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(None))
  val x2 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(Some(("B","two"))))
  val x3 = Seq[Try[Option[(String, Any)]]](Success(Some(("A",1))), Success(Some(("B","two"))), Failure(new Exception("bad")))
  def f(x: Seq[Try[Option[(String, Any)]]]) =
    x.find( _.isFailure ).getOrElse( Success(Some(x.map( _.get ).filterNot( _.isEmpty ).map( _.get ).toMap)) )
}

Пример сеанса

bash-3.2$ scalac foo.scala
bash-3.2$ scala -classpath .
Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
Type in expressions for evaluation. Or try :help.

scala> import foo.foo._
import foo.foo._

scala> f(x0)
res0: scala.util.Try[Option[Equals]] = Success(Some(Map()))

scala> f(x1)
res1: scala.util.Try[Option[Equals]] = Success(Some(Map(A -> 1)))

scala> f(x2)
res2: scala.util.Try[Option[Equals]] = Success(Some(Map(A -> 1, B -> two)))

scala> f(x3)
res3: scala.util.Try[Option[Equals]] = Failure(java.lang.Exception: bad)

scala> :quit
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...