scala either

scala - ¿La mejor manera de convertir una lista de Eithers en una de las listas?



(7)

A partir de Scala 2.13 , la mayoría de las colecciones ahora cuentan con un método de partición Con elementos de partición basados ​​en una función que devuelve Right o Left .

En nuestro caso, ni siquiera necesitamos una función que transforme nuestra entrada en Right o Left para definir la partición ya que ya tenemos Right e Left s. ¡Así un simple uso de la identity !

Entonces es una cuestión simple de hacer coincidir la tupla particionada resultante de izquierdas y derechos en función de si hay izquierdas o no:

eithers.partitionWith(identity) match { case (Nil, rights) => Right(rights) case (lefts, _) => Left(lefts) } // * List[Either[String, Int]] = List(Right(3), Left("error x"), Right(7)) // => Either[List[String],List[Int]] = Left(List(error x)) // * List[Either[String, Int]] = List(Right(3), Right(7)) // => Either[List[String],List[Int]] = Right(List(3, 7))

Para entender la partitionWith aquí está el resultado del paso intermedio:

List(Right(3), Left("error x"), Right(7)).partitionWith(identity) // (List[String], List[Int]) = (List(error x), List(3, 7))

Tengo un código como el que aparece a continuación, donde tengo una lista de Eithers, y quiero convertirlo en una de las listas ... en particular (en este caso), si hay Lefts en la lista, entonces regreso a la izquierda de la lista de ellos, de lo contrario devuelvo un derecho de la lista de los derechos.

val maybe: List[Either[String, Int]] = getMaybe val (strings, ints) = maybe.partition(_.isLeft) strings.map(_.left.get) match { case Nil => Right(ints.map(_.right.get)) case stringList => Left(stringList) }

Llamar a get siempre me hace sentir que debo estar perdiendo algo.

¿Hay una manera más idiomática de hacer esto?


No quiero ningún tipo de karma para esto ya que es una combinación de la respuesta de Chris y la de Viktor desde here ... pero aquí hay una alternativa:

def split[CC[X] <: Traversable[X], A, B](xs: CC[Either[A, B]]) (implicit bfa: CanBuildFrom[Nothing, A, CC[A]], bfb: CanBuildFrom[Nothing, B, CC[B]]) : (CC[A], CC[B]) = xs.foldLeft((bfa(), bfb())) { case ((as, bs), l@Left(a)) => (as += a, bs) case ((as, bs), r@Right(b)) => (as, bs += b) } match { case (as, bs) => (as.result(), bs.result()) }

Ejemplo:

scala> val eithers: List[Either[String, Int]] = List(Left("Hi"), Right(1)) eithers: List[Either[String,Int]] = List(Left(Hi), Right(1)) scala> split(eithers) res0: (List[String], List[Int]) = (List(Hi),List(1))


Puede escribir una versión generalizada de split siguiente manera:

def split[X, CC[X] <: Traversable[X], A, B](l : CC[Either[A, B]]) (implicit bfa : CanBuildFrom[Nothing, A, CC[A]], bfb : CanBuildFrom[Nothing, B, CC[B]]) : (CC[A], CC[B]) = { def as = { val bf = bfa() bf ++= (l collect { case Left(x) => x}) bf.result } def bs = { val bf = bfb() bf ++= (l collect { case Right(x) => x}) bf.result } (as, bs) }

Tal que

scala> List(Left("x"),Right(2), Right(4)) : List[Either[java.lang.String,Int]] res11: List[Either[java.lang.String,Int]] = List(Left(x), Right(2), Right(4)) scala> split(res11) res12: (List[java.lang.String], List[Int]) = (List(x),List(2, 4)) scala> Set(Left("x"),Right(2), Right(4)) : Set[Either[java.lang.String,Int]] res13: Set[Either[java.lang.String,Int]] = Set(Left(x), Right(2), Right(4)) scala> split(res13) res14: (Set[java.lang.String], Set[Int]) = (Set(x),Set(2, 4))


Si quieres tener algo más general y también funcional, entonces la biblioteca Validated desde los gatos es del tipo que deseas. Es algo así como lo que puede agregar errores. Y en combinación con NonEmptyList puede ser realmente poderoso.

http://typelevel.org/cats/datatypes/validated.html


Solución de Programación Funcional en libro Scala.

def sequence[E,A](es: List[Either[E,A]]): Either[E,List[A]] = traverse(es)(x => x) def traverse[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = es match { case Nil => Right(Nil) case h::t => (f(h) map2 traverse(t)(f))(_ :: _) } def map2[EE >: E, B, C](a: Either[E, A], b: Either[EE, B])(f: (A, B) => C): Either[EE, C] = for { a1 <- a; b1 <- b } yield f(a1,b1)


data.partition(_.isLeft) match { case (Nil, ints) => Right(for(Right(i) <- ints) yield i) case (strings, _) => Left(for(Left(s) <- strings) yield s) }

Para una pasada:

data.partition(_.isLeft) match { case (Nil, ints) => Right(for(Right(i) <- ints.view) yield i) case (strings, _) => Left(for(Left(s) <- strings.view) yield s) }


val list = List(Left("x"),Right(2), Right(4)) val strings = for (Left(x) <- list) yield(x) val result = if (strings.isEmpty) Right(for (Right(x) <- list) yield(x)) else Left(strings)