29

I want to split a List[Either[A, B]] in two lists.

Is there a better way ?

def lefts[A, B](eithers : List[Either[A, B]]) : List[A] = eithers.collect { case Left(l) => l}
def rights[A, B](eithers : List[Either[A, B]]) : List[B] = eithers.collect { case Right(r) => r}
Yann Moisan
  • 8,161
  • 8
  • 47
  • 91

9 Answers9

37

Starting Scala 2.13, most collections are now provided with a partitionMap method which partitions elements based on a function which returns 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 as we already have Rights and Lefts. Thus a simple use of identity:

val (lefts, rights) = List(Right(2), Left("a"), Left("b")).partitionMap(identity)
// lefts: List[String] = List(a, b)
// rights: List[Int] = List(2)
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
26

Not sure this is really much neater, but :

scala> def splitEitherList[A,B](el: List[Either[A,B]]) = {
         val (lefts, rights) = el.partition(_.isLeft)
         (lefts.map(_.left.get), rights.map(_.right.get))
       }
splitEitherList: [A, B](el: List[Either[A,B]])(List[A], List[B])

scala> val el : List[Either[Int, String]] = List(Left(1), Right("Success"), Left(42))
el: List[Either[Int,String]] = List(Left(1), Right(Success), Left(42))

scala> val (leftValues, rightValues) = splitEitherList(el)
leftValues: List[Int] = List(1, 42)
rightValues: List[String] = List("Success")
Marth
  • 23,920
  • 3
  • 60
  • 72
  • 3
    Edit : (lefts.map(_.left.get), rights.map(_.right.get)) becomes (lefts.flatMap(_.left.toOption), rights.flatMap(_.right.toOption)) – Lombric Jun 14 '17 at 13:22
19

If scalaz is one of your dependencies I would simply use separate:

import scalaz.std.list._
import scalaz.std.either._
import scalaz.syntax.monadPlus._

val el : List[Either[Int, String]] = List(Left(1), Right("Success"), Left(42))

scala> val (lefts, rights) = el.separate
lefts: List[Int] = List(1, 42)
rights: List[String] = List(Success)
aperc
  • 23
  • 4
Filippo Vitale
  • 7,597
  • 3
  • 58
  • 64
  • 3
    Care to provide a more complete answer with all required imports? And the specific scalaz version? – noahlz Feb 10 '16 at 06:22
  • 1
    `libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.2.2"`, and `import scalaz.Scalaz._`. more info: http://stackoverflow.com/questions/36878459/how-to-turn-a-list-of-eithers-to-a-either-of-lists-using-scalaz-monadplus-separa/36883766#36883766 – David Portabella Apr 28 '16 at 21:21
  • 12
    And in case you use `cats`, you have `separate` too! http://typelevel.org/cats/api/cats/syntax/SeparateOps.html#separate(implicitF:cats.MonadCombine[F],implicitG:cats.Bifoldable[G]):(F[A],F[B]) – Gabriele Petronella Aug 02 '17 at 09:27
9

You can do it with:

val (lefts, rights) = eithers.foldRight((List[Int](), List[String]()))((e, p) => e.fold(l => (l :: p._1, p._2), r => (p._1, r :: p._2)))
roterl
  • 1,883
  • 14
  • 24
9

A compact, but not CPU-efficient solution:

val lefts = list.flatMap(_.left.toOption)
val rights = list.flatMap(_.right.toOption)
VasiliNovikov
  • 9,681
  • 4
  • 44
  • 62
1

Well, in case it doesn't have to be a one-liner... then it can be a no-brainer.

def split[A,B](eithers : List[Either[A, B]]):(List[A],List[B]) = {
  val lefts = scala.collection.mutable.ListBuffer[A]()
  val rights = scala.collection.mutable.ListBuffer[B]()
  eithers.map {
    case Left(l) => lefts += l
    case Right(r) => rights += r
  }
  (lefts.toList, rights.toList)
}

But, to be honest, I'd prefer Marth's answer :)

makingthematrix
  • 385
  • 1
  • 5
1

If you're going to bother to abstract the functionality, as in Marth's answer, then it may actually make more sense to use roterl's solution:

def splitEitherList[A,B](el: List[Either[A,B]]): (List[A], List[B]) =
  (el :\ (List[A](), List[B]()))((e, p) =>
    e.fold(l => (l :: p._1, p._2), r => (p._1, r :: p._2)))

val x = List(Left(1), Right(3), Left(2), Left(4), Right(8))
splitEitherList(x) // (List(1, 2, 4), List(3, 8))

This way awards more functional brownie points, but also may be more performant, as it makes use of a right fold to create the lists in one pass

But if you're doing it on the fly and/or find folds difficult to read, then by all means

el.partition(_.isLeft) match { case (lefts, rights) =>
  (lefts.map(_.left.get), rights.map(_.right.get)) }
Daniel Avery
  • 159
  • 1
  • 4
0

A somewhat functional solution for Seq.

def partition[A, B](seq: Seq[Either[A, B]]): (Seq[A], Seq[B]) = {
  seq.foldLeft[(Seq[A], Seq[B])]((Nil, Nil)) { case ((ls, rs), next) =>
    next match {
      case Left(l) => (ls :+ l, rs)
      case Right(r) => (ls, rs :+ r)
    }
  }
}
Nikita Gousak
  • 128
  • 2
  • 8
0

Similar to Scalaz for Cats you can also use separate

import cats.implicits._
val items = List(Right(1), Left("error"), Right(2), Right(3), Left("another error"))

val groupedItems: (List[String], List[Int]) = items.separate

//(List(error, another error),List(1, 2, 3))
Llewellyn
  • 651
  • 2
  • 8
  • 9
  • I missed that this answer is already in the comments of https://stackoverflow.com/questions/26576530/how-to-split-a-listeithera-b/72651774#answer-30960721 – Llewellyn Jun 20 '22 at 06:33