我有一些代码,如下所示,我有一个Eithers列表,我想把它变成一个列表中的一个......特别是(在这种情况下),如果列表中有任何Lefts,然后我返回列表中的左侧,否则我返回权利列表中的权利。
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)
}
调用get
总让我觉得我必须遗漏一些东西。
是否有更惯用的方式来做到这一点?
答案 0 :(得分:21)
data.partition(_.isLeft) match {
case (Nil, ints) => Right(for(Right(i) <- ints) yield i)
case (strings, _) => Left(for(Left(s) <- strings) yield s)
}
一次通过:
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)
}
答案 1 :(得分:8)
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)
答案 2 :(得分:5)
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)
答案 3 :(得分:5)
您可以编写split
的通用版本,如下所示:
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)
}
这样:
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))
答案 4 :(得分:3)
从Scala 2.13
开始,现在大多数集合都提供了partitionMap
方法,该方法根据返回Right
或Left
的函数对元素进行分区。
在我们的情况下,我们甚至不需要一个将输入转换为Right
或Left
来定义分区的函数,因为我们已经有Right
和{{1} } s。因此,简单使用Left
!
这是一个简单的问题,即根据是否有剩余来匹配结果的左和右分区元组:
identity
为理解eithers.partitionMap(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))
,这是中间步骤的结果:
partitionMap
答案 5 :(得分:2)
我有点不想要任何业力,因为它是Chris's answer和Viktor来自here的合并......但这里有另一种选择:
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())
}
示例:
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))
答案 6 :(得分:1)
如果你想拥有一些更通用且功能更强的东西,那么来自猫库的Validated
就是你想要的类型。它类似于Either
,可以聚合错误。与NonEmptyList
结合使用它可以非常强大。
答案 7 :(得分:1)
这不是更优雅的方式吗?
def flatten[E,A](es: List[Either[E,A]]): Either[E,List[A]] = {
@tailrec
def go(tail: List[Either[E,A]], acc: List[A]): Either[E,List[A]] = tail match {
case Nil => Right(acc)
case h::t => h match {
case Left(e) => Left(e)
case Right(a) => go(t, a :: acc)
}
}
go(es, Nil) map { _ reverse }
}
a :: acc
是子弹般快速partitionMap
可能更快,因为基于builder的内部实现答案 8 :(得分:0)
要分别提取“左”和“右”:
val data: List[Either[String, Int]] = List(
Right(1),
Left("Error #1"),
Right(42),
Left("Error #2")
)
val numbers: List[Int] = data.collect { case Right(value) => value }
val errors: List[String] = data.collect { case Left(error) => error }
println(numbers) // List(1, 42)
println(errors) // List(Error #1, Error #2)