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
My answer is similar to @Garrett Rowe's: But it uses foldLeft (Also see: Why foldRight and reduceRight are NOT tail recursive?) and prepends to Seq rather than appending to Seq (See: Why is appending to a list bad?).
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))
Building on Kevin's solution, and stealing a bit from Haskell's Either type, you can create a method partitionEithers like so:
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))
}
And use that to build your solution
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)
}