Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 get a Right (which will be a Seq[A]), if all elements of the sequence are Right. If there is at least one Left (an error message), I'd like to obtain the first error message or a concatenation of all error messages.

Of course you can post cats or scalaz code but I'm also interested in code not using it.

Edit

I've changed the title, which originally asked for an Either[Seq[A],Seq[B]] to reflect the body of the message.

like image 685
ziggystar Avatar asked Aug 29 '11 13:08

ziggystar


People also ask

What is the difference between SEQ and list?

A Seq is an Iterable that has a defined order of elements. Sequences provide a method apply() for indexing, ranging from 0 up to the length of the sequence. Seq has many subclasses including Queue, Range, List, Stack, and LinkedList. A List is a Seq that is implemented as an immutable linked list.

What does seq mean in Scala?

Scala Seq is a trait to represent immutable sequences. This structure provides index based access and various utility methods to find elements, their occurences and subsequences. A Seq maintains the insertion order.

What is seq of string?

Seq is a trait which represents indexed sequences that are guaranteed immutable. You can access elements by using their indexes. It maintains insertion order of elements. Sequences support a number of methods to find occurrences of elements or subsequences.


2 Answers

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)) 
like image 166
12 revs Avatar answered Sep 19 '22 05:09

12 revs


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

like image 33
Kevin Wright Avatar answered Sep 23 '22 05:09

Kevin Wright