Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to turn a Lists of Eithers into an Either of Lists?

Tags:

scala

either

I have some code like the below, where I have a list of Eithers, and I want to turn it into an Either of Lists ... in particular (in this case), if there are any Lefts in the list, then I return a Left of the list of them, otherwise I return a Right of the list of the rights.

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

Calling get always makes me feel like I must be missing something.

Is there a more idiomatic way to do this?

like image 260
Eric Bowman - abstracto - Avatar asked Jun 27 '11 07:06

Eric Bowman - abstracto -


2 Answers

data.partition(_.isLeft) match {                               case (Nil,  ints) => Right(for(Right(i) <- ints) yield i)           case (strings, _) => Left(for(Left(s) <- strings) yield s) } 

For one pass:

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) } 
like image 196
Viktor Klang Avatar answered Sep 19 '22 23:09

Viktor Klang


Starting in Scala 2.13, most collections are now provided with a partitionMap method which partitions elements based on a function returning 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 any lefts:

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

For the understanding of partitionMap here is the result of the intermediate step:

List(Right(3), Left("error x"), Right(7)).partitionMap(identity) // (List[String], List[Int]) = (List(error x), List(3, 7)) 
like image 24
Xavier Guihot Avatar answered Sep 19 '22 23:09

Xavier Guihot