Michał Jurczuk
Michał Jurczuk

Reputation: 3828

Change list of Eithers to two value lists in scala

How can I change list of Eithers into two list of value Right and Left. When I use partition it returns two lists of Either's not values. What is the simplest way to do it?

Upvotes: 2

Views: 218

Answers (4)

elm
elm

Reputation: 20435

Using for comprehensions, like this,

for ( Left(v) <- xs ) yield v

and

for ( Right(v) <- xs ) yield v

Upvotes: 1

wingedsubmariner
wingedsubmariner

Reputation: 13667

foldLeft allows you to easily write your own method:

def separateEithers[T, U](list: List[Either[T, U]]) = {
  val (ls, rs) = list.foldLeft(List[T](), List[U]()) {
    case ((ls, rs), Left(x)) => (x :: ls, rs)
    case ((ls, rs), Right(x)) => (ls, x :: rs)
  }
  (ls.reverse, rs.reverse)
}

Upvotes: 6

om-nom-nom
om-nom-nom

Reputation: 62855

If making two passes through the list is okay for you, you can use collect:

type E = Either[String, Int]
val xs: List[E] = List(Left("foo"), Right(1), Left("bar"), Right(2))
val rights = xs.collect { case Right(x) => x}
// rights: List[Int] = List(1, 2)

val lefts = xs.collect { case Left(x) => x}
// lefts: List[String] = List(foo, bar)

Upvotes: 2

sjrd
sjrd

Reputation: 22105

You'll have to map the two resulting lists after partitioning.

val origin: List[Either[A, B]] = ???
val (lefts, rights) = origin.partition(_.isInstanceOf[Left[_]])
val leftValues = lefts.map(_.asInstanceOf[Left[A]].a)
val rightValues = rights.map(_.asInstanceOf[Right[B]].b)

If you are not happy with the casts and isInstanceOf's, you can also do it in two passes:

val leftValues = origin collect {
  case Left(a) => a
}
val rightValues = origin collect {
  case Right(b) => b
}

And if you are not happy with the two passes, you'll have to do it "by hand":

def myPartition[A, B](origin: List[Either[A, B]]): (List[A], List[B]) = {
  val leftBuilder = List.newBuilder[A]
  val rightBuilder = List.newBuilder[B]
  origin foreach {
    case Left(a)  => leftBuilder += a
    case Right(b) => rightBuilder += b
  }
  (leftBuilder.result(), rightBuilder.result())
}

Finally, if you don't like mutable state, you can do so:

def myPartition[A, B](origin: List[Either[A, B]]): (List[A], List[B]) = {
  @tailrec
  def loop(xs: List[Either[A, B]], accLeft: List[A],
      accRight: List[B]): (List[A], List[B]) = {
    xs match {
      case Nil            => (accLeft.reverse, accRight.reverse)
      case Left(a) :: xr  => loop(xr, a :: accLeft, accRight)
      case Right(b) :: xr => loop(xr, accLeft, b :: accRight)
    }
  }
  loop(origin, Nil, Nil)
}

Upvotes: 2

Related Questions