Search code examples
scalaflattenscala-catseither

Flatten List[Either[A, B]] to List[B] like List[Option[B]] to List[B]


Does Cats provide flattening akin to

implicit class FlattenListOfEither[L, R](l: List[Either[L, R]]) {
  def flattenM: List[R] = l collect { case Right(v) => v }
}

such that

val l1: List[Either[String, Int]] = List(Right(1), Right(2), Left("error"), Right(4))
l1.flattenM

outputs

List(1, 2, 4)

similar to how vanilla Scala flattens list of options

val l2: List[Option[Int]] = List(Some(1), Some(2), None, Some(4))
l2.flatten

which outputs

List(1, 2, 4)

separate gives the following syntax

import cats.implicits._
val (_, rights) = l1.separate
rights

which outputs

List(1, 2, 4)

however does there exist out-of-the-box flatten-like extension method which returns just the rights instead of the tuple?


Solution

  • I think the easiest is to use mapFilter provided by the FunctorFilter typeclass. It looks like this:

    def mapFilter[A, B](fa: F[A])(f: (A) ⇒ Option[B]): F[B]
    

    Where F[A] could be List[A], or Vector[A] or any other type that allows filtering.

    If we apply this function to your list, we just need to turn the Either[A, B]s into Option[B]s. This is as easy as calling toOption. With that the full solution looks like this:

    import cats.implicits._
    
    val l1: List[Either[String, Int]] = List(Right(1), Right(2), Left("error"), Right(4))
    
    l1.mapFilter(_.toOption)
    // List(1, 2, 4)