K  D
K D

Reputation: 314

Scala map only right value and display error for left

I have a sequence of left and right values like:

val l: Seq[Either[Error, Data]] = Seq(Left(Error), Right(Data), ...)

I want to map all Right values and display the error for a Left. I have tried:

val data: Seq[Data] = l.flatMap {
  case Right(data) => data
  case Left(err)   => println(err) // doesn't work because println is Unit
}

Any way to do this?

Upvotes: 1

Views: 1081

Answers (2)

jwvh
jwvh

Reputation: 51271

You were on the right track, just need to let flatMap() remove the Error after it's been printed.

val data: Seq[Data] = l.flatMap {
  case Right(data) => Some(data)
  case Left(err)   => println(err); None
}

A single traversal is all that's needed.

Upvotes: 1

Levi Ramsey
Levi Ramsey

Reputation: 20561

It's generally not a great practice to mix side effects and pure code like this, but something like (assuming a strict Seq):

def rightsAfterEffectingLefts[A, B](eithers: Seq[Either[A, B]])(effect: A => Unit): Seq[B] = {
  eithers.foreach(_.left.foreach(effect))
  eithers.flatMap(_.toOption)
}

val data = rightsAfterEffectingLefts(l)(println _)

It's possible to optimize to avoid the double iteration, though you'd likely want to approach different Seq implementations differently.

EDIT: after Luis's suggestion

def rightsAfterEffectingLefts[A, B](eithers: Seq[Either[A, B]])(effect: A => Unit): Seq[B] = {
  val (lefts, rights) = eithers.partition(_.isLeft)
  lefts.foreach(_.left.foreach(effect))
  rights.flatMap(_.toOption)
}

is an alternative definition. It still double iterates and will likely be slower.

Upvotes: 2

Related Questions