[英]How to split a List[Either[A, B]]
I want to split a List[Either[A, B]]
in two lists.我想将List[Either[A, B]]
拆分为两个列表。
Is there a better way ?有没有更好的办法 ?
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}
Starting Scala 2.13
, most collections are now provided with a partitionMap
method which partitions elements based on a function which returns either Right
or Left
.从Scala 2.13
开始,大多数集合现在都提供了partitionMap
方法,该方法基于返回Right
或Left
的函数对元素进行分区。
In our case, we don't even need a function that transforms our input into Right
or Left
to define the partitioning as we already have Right
s and Left
s.在我们的例子中,我们甚至不需要将输入转换为Right
或Left
的函数来定义分区,因为我们已经有了Right
和Left
。 Thus a simple use of identity
:因此identity
的简单使用:
val (lefts, rights) = List(Right(2), Left("a"), Left("b")).partitionMap(identity)
// lefts: List[String] = List(a, b)
// rights: List[Int] = List(2)
Not sure this is really much neater, but :不确定这是否真的更整洁,但是:
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")
If scalaz
is one of your dependencies I would simply use separate
:如果scalaz
是您的依赖项之一,我将简单地使用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)
你可以这样做:
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)))
A compact, but not CPU-efficient solution:一个紧凑但 CPU 效率不高的解决方案:
val lefts = list.flatMap(_.left.toOption)
val rights = list.flatMap(_.right.toOption)
Well, in case it doesn't have to be a one-liner... then it can be a no-brainer.好吧,如果它不必是单线……那么它可以是不费吹灰之力的。
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)
}
But, to be honest, I'd prefer Marth 's answer :)但是,老实说,我更喜欢Marth的回答 :)
If you're going to bother to abstract the functionality, as in Marth's answer, then it may actually make more sense to use roterl's solution:如果您打算像 Marth 的回答那样费心抽象功能,那么使用 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))
This way awards more functional brownie points, but also may be more performant, as it makes use of a right fold to create the lists in one pass这种方式奖励更多功能性的布朗尼积分,但也可能更高效,因为它利用正确的折叠来一次创建列表
But if you're doing it on the fly and/or find folds difficult to read, then by all means但是,如果您是在运行中进行操作和/或发现折叠难以阅读,那么请务必
el.partition(_.isLeft) match { case (lefts, rights) =>
(lefts.map(_.left.get), rights.map(_.right.get)) }
A somewhat functional solution for Seq
. 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)
}
}
}
Similar to Scalaz for Cats you can also use separate
与 Scalaz for Cats 类似,您也可以separate
使用
import cats.implicits._
val items = List(Right(1), Left("error"), Right(2), Right(3), Left("another error"))
val groupedItems: (List[String], List[Int]) = items.separate
//(List(error, another error),List(1, 2, 3))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.