scala either

scala - Cómo dividir una lista[O bien



either (8)

Quiero dividir una List[Either[A, B]] en dos listas.

¿Hay alguna manera mejor?

def lefts[A, B](eithers : List[Either[A, B]]) : List[A] = eithers.collect { case Left(l) => l} def rights[A, B](eithers : List[Either[A, B]]) : List[B] = eithers.collect { case Right(r) => r}


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 . Así, un simple uso de la identity :

val (lefts, rights) = List(Right(2), Left("a"), Left("b")).partitionWith(identity) // lefts: List[String] = List(a, b) // rights: List[Int] = List(2)


Bueno, en caso de que no tenga que ser de una sola línea ... entonces puede ser una obviedad.

def split[A,B](eithers : List[Either[A, B]]):(List[A],List[B]) = { val lefts = scala.collection.mutable.ListBuffer[A]() val rights = scala.collection.mutable.ListBuffer[B]() eithers.map { case Left(l) => lefts += l case Right(r) => rights += r } (lefts.toList, rights.toList) }

Pero, para ser honesto, preferiría la respuesta de Marth :)


No estoy seguro de que esto sea realmente mucho más limpio, pero:

scala> def splitEitherList[A,B](el: List[Either[A,B]]) = { val (lefts, rights) = el.partition(_.isLeft) (lefts.map(_.left.get), rights.map(_.right.get)) } splitEitherList: [A, B](el: List[Either[A,B]])(List[A], List[B]) scala> val el : List[Either[Int, String]] = List(Left(1), Right("Success"), Left(42)) el: List[Either[Int,String]] = List(Left(1), Right(Success), Left(42)) scala> val (leftValues, rightValues) = splitEitherList(el) leftValues: List[Int] = List(1, 42) rightValues: List[String] = List("Success")


Puedes hacerlo con:

val (lefts, rights) = eithers.foldRight((List[Int](), List[String]()))((e, p) => e.fold(l => (l :: p._1, p._2), r => (p._1, r :: p._2)))


Si scalaz es una de sus dependencias, simplemente usaría por separate :

import scalaz.std.list._ import scalaz.std.either._ import scalaz.syntax.monadPlus._ val el : List[Either[Int, String]] = List(Left(1), Right("Success"), Left(42)) scala> val (lefts, rights) = el.separate lefts: List[Int] = List(1, 42) rights: List[String] = List(Success)


Si se va a molestar en abstraer la funcionalidad, como en la respuesta de Marth, entonces puede que tenga más sentido usar la solución de roterl:

def splitEitherList[A,B](el: List[Either[A,B]]): (List[A], List[B]) = (el :/ (List[A](), List[B]()))((e, p) => e.fold(l => (l :: p._1, p._2), r => (p._1, r :: p._2))) val x = List(Left(1), Right(3), Left(2), Left(4), Right(8)) splitEitherList(x) // (List(1, 2, 4), List(3, 8))

De esta manera, se otorgan puntos brownie más funcionales, pero también puede ser más eficaz, ya que utiliza un pliegue a la derecha para crear las listas en una sola pasada.

Pero si lo haces sobre la marcha y / o encuentras pliegues difíciles de leer, entonces por todos los medios

el.partition(_.isLeft) match { case (lefts, rights) => (lefts.map(_.left.get), rights.map(_.right.get)) }


Una solución algo funcional para Seq .

def partition[A, B](seq: Seq[Either[A, B]]): (Seq[A], Seq[B]) = { seq.foldLeft[(Seq[A], Seq[B])]((Nil, Nil)) { case ((ls, rs), next) => next match { case Left(l) => (ls :+ l, rs) case Right(r) => (ls, rs :+ r) } } }


Una solución compacta, pero no eficiente para la CPU:

val lefts = list.flatMap(_.left.toOption) val rights = list.flatMap(_.right.toOption)