52

Seq[Either[String,A]]给定一个带有Left错误消息的序列。如果序列Either[String,Seq[A]]的所有元素都是. 如果至少有一个(错误消息),我想获得第一条错误消息或所有错误消息的串联。RightSeq[A]RightLeft

当然,您可以发布猫或 scalaz 代码,但我也对不使用它的代码感兴趣。

编辑

我已经更改了标题,它最初要求一个Either[Seq[A],Seq[B]]来反映消息的正文。

4

8 回答 8

33

编辑:我错过了您要求的问题的标题Either[Seq[A],Seq[B]],但我确实阅读了“我想获得第一条错误消息或所有错误消息的串联”,这会给您前者:

def sequence[A, B](s: Seq[Either[A, B]]): Either[A, Seq[B]] =
  s.foldRight(Right(Nil): Either[A, List[B]]) {
    (e, acc) => for (xs <- acc.right; x <- e.right) yield x :: xs
  }

scala> sequence(List(Right(1), Right(2), Right(3)))
res2: Either[Nothing,Seq[Int]] = Right(List(1, 2, 3))

scala> sequence(List(Right(1), Left("error"), Right(3)))
res3: Either[java.lang.String,Seq[Int]] = Left(error)

使用 Scalaz:

val xs: List[Either[String, Int]] = List(Right(1), Right(2), Right(3))

scala> xs.sequenceU
res0:  scala.util.Either[String,List[Int]] = Right(List(1, 2, 3))
于 2011-08-29T14:02:51.293 回答
16

给定一个起始序列xs,这是我的看法:

xs collectFirst { case x@Left(_) => x } getOrElse
  Right(xs collect {case Right(x) => x})

这是对问题主体的回答,仅将第一个错误作为Either[String,Seq[A]]. 这显然不是标题中问题的有效答案


返回所有错误:

val lefts = xs collect {case Left(x) => x }
def rights = xs collect {case Right(x) => x}
if(lefts.isEmpty) Right(rights) else Left(lefts)

请注意,它rights被定义为一种方法,因此仅在需要时才对其进行评估

于 2011-08-29T19:00:07.813 回答
11

它应该工作:

def unfoldRes[A](x: Seq[Either[String, A]]) = x partition {_.isLeft} match {
  case (Nil, r) => Right(r map {_.right.get})
  case (l, _) => Left(l map {_.left.get} mkString "\n")
}

您将结果拆分为左右,如果左边为空,则构建一个右,否则,构建一个左。

于 2011-08-29T14:02:48.017 回答
8

这是scalaz代码:

_.sequence

于 2011-09-15T09:52:48.743 回答
6

从 开始Scala 2.13,大多数集合都提供了一种partitionMap方法,该方法基于将项目映射到Right或的函数来划分元素Left

在我们的例子中,我们甚至不需要将输入转换为RightLeft定义分区的函数,因为我们已经有了Rights 和Lefts。因此,一个简单的使用identity

然后,只需根据是否存在左侧来匹配生成的左侧和右侧分区元组:

eithers.partitionMap(identity) match {
  case (Nil, rights)       => Right(rights)
  case (firstLeft :: _, _) => Left(firstLeft)
}

// * val eithers: List[Either[String, Int]] = List(Right(1), Right(2), Right(3))
//         => Either[String,List[Int]] = Right(List(1, 2, 3))
// * val eithers: List[Either[String, Int]] = List(Right(1), Left("error1"), Right(3), Left("error2"))
//         => Either[String,List[Int]] = Left("error1")

中间步骤 ( partitionMap) 的详细信息:

List(Right(1), Left("error1"), Right(3), Left("error2")).partitionMap(identity)
// => (List[String], List[Int]) = (List("error1", "error2"), List(1, 3))
于 2019-01-17T20:56:49.460 回答
2

基于 Kevin 的解决方案,并从 Haskell 的 Either 类型中窃取一点,您可以创建一个方法 partitionEithers,如下所示:

def partitionEithers[A, B](es: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
  es.foldRight (Seq.empty[A], Seq.empty[B]) { case (e, (as, bs)) =>
    e.fold (a => (a +: as, bs), b => (as, b +: bs))
  }

并使用它来构建您的解决方案

def unroll[A, B](es: Seq[Either[A, B]]): Either[Seq[A], Seq[B]] = {
  val (as, bs) = partitionEithers(es)
  if (!as.isEmpty) Left(as) else Right(bs)
}
于 2011-08-29T20:15:53.507 回答
0

我不习惯使用 Either - 这是我的方法;也许有更优雅的解决方案:

def condense [A] (sesa: Seq [Either [String, A]]): Either [String, Seq [A]] = {
  val l = sesa.find (e => e.isLeft)
  if (l == None) Right (sesa.map (e => e.right.get)) 
  else Left (l.get.left.get)
}

condense (List (Right (3), Right (4), Left ("missing"), Right (2)))
// Either[String,Seq[Int]] = Left(missing)
condense (List (Right (3), Right (4), Right (1), Right (2)))
// Either[String,Seq[Int]] = Right(List(3, 4, 1, 2))

Left (l.get.left.get)看起来有点滑稽,但l它本身是一个 Either [A, B],而不是一个 Either [A, Seq[B]],需要重新包装。

于 2011-08-29T15:35:04.020 回答
0

我的回答类似于@Garrett Rowe 的:但它使用 foldLeft(另请参阅:为什么 foldRight 和 reduceRight 不是尾递归?)并预先添加到 Seq 而不是附加到 Seq(请参阅:为什么附加到列表不好?)。

scala> :paste
// Entering paste mode (ctrl-D to finish)

def partitionEitherSeq[A,B](eitherSeq: Seq[Either[A,B]]): (Seq[A], Seq[B]) =
  eitherSeq.foldLeft(Seq.empty[A], Seq.empty[B]) { (acc, next) =>
  val (lefts, rights) = acc
  next.fold(error => (lefts :+ error, rights), result => (lefts, rights :+ result))
}

// Exiting paste mode, now interpreting.

partitionEitherSeq: [A, B](eitherSeq: Seq[Either[A,B]])(Seq[A], Seq[B])

scala> partitionEitherSeq(Seq(Right("Result1"), Left("Error1"), Right("Result2"), Right("Result3"), Left("Error2")))
res0: (Seq[java.lang.String], Seq[java.lang.String]) = (List(Error1, Error2),List(Result1, Result2, Result3))
于 2014-01-07T23:40:00.623 回答