How to reduce Seq[Either[A,B]] to Either[A,Seq[B]]?

前端 未结 8 1378
孤城傲影
孤城傲影 2020-12-05 01:31

Given a sequence of eithers Seq[Either[String,A]] with Left being an error message. I want to obtain an Either[String,Seq[A]] where I

相关标签:
8条回答
  • 2020-12-05 02:15

    Given a starting sequence xs, here's my take:

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

    This being in answer to the body of the question, obtaining only the first error as an Either[String,Seq[A]]. It's obviously not a valid answer to the question in the title


    To return all errors:

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

    Note that rights is defined as a method, so it'll only be evaluated on demand, if necessary

    0 讨论(0)
  • 2020-12-05 02:16

    It should work:

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

    You split your result in left and right, if left is empty, build a Right, otherwise, build a left.

    0 讨论(0)
  • 2020-12-05 02:17

    Edit: I missed that the title of your question asked for Either[Seq[A],Seq[B]], but I did read "I'd like to obtain the first error message or a concatenation of all error messages", and this would give you the former:

    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)
    

    Using 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))
    
    0 讨论(0)
  • 2020-12-05 02:17

    Starting in Scala 2.13, most collections are provided with a partitionMap method which partitions elements based on a function which maps items to either Right or Left.

    In our case, we don't even need a function that transforms our input into Right or Left to define the partitioning since we already have Rights and Lefts. Thus a simple use of identity!

    Then it's just a matter of matching the resulting partitioned tuple of lefts and rights based on whether or not there are lefts:

    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")
    

    Details of the intermediate step (partitionMap):

    List(Right(1), Left("error1"), Right(3), Left("error2")).partitionMap(identity)
    // => (List[String], List[Int]) = (List("error1", "error2"), List(1, 3))
    
    0 讨论(0)
  • 2020-12-05 02:17

    I'm not used to use Either - here is my approach; maybe there are more elegant solutions:

    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) looks a bit funny, but l itself is a Either [A, B], not an Either [A, Seq[B]], and needs rewrapping.

    0 讨论(0)
  • 2020-12-05 02:25

    Here is the scalaz code:

    _.sequence

    0 讨论(0)
提交回复
热议问题