'Get only Right values from Either sequence
This code prints List(1, 2, ())
def f1(i:Int): Either[String,Int] = if (i > 0) Right(1) else Left("error 1")
def f2(i:Int): Either[String,Int] = if (i > 0) Right(2) else Left("error 2")
def f3(i:Int): Either[String,Int] = if (i > 0) Right(3) else Left("error 3")
val seq = Seq(f1(1),f2(1),f3(-1))
val x = seq.map { f => if (f.isRight) f.right.get }
println(x)
I need an array filtered by isRight
is true, eliminating any other values. In the example above, the result I would need is List(1, 2)
. How to achieve it?
Solution 1:[1]
You have to remember that in Scala, if
is an expression that returns a value. In your case, the value is either f.right.get
(if Right
is present in Either
), or Unit
otherwise. To filter only on Right
values, use filter
:
val x = seq.filter(_.isRight).map(_.right.get)
Solution 2:[2]
Use collect
and pattern match to select the case you are interested in.
seq.collect { case Right(value) => value}
Lets try on
Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
def f1(i:Int): Either[String,Int] = if (i > 0) Right(1) else Left("error 1")
def f2(i:Int): Either[String,Int] = if (i > 0) Right(2) else Left("error 2")
def f3(i:Int): Either[String,Int] = if (i > 0) Right(3) else Left("error 3")
val seq = Seq(f1(1),f2(1),f3(-1))
// Exiting paste mode, now interpreting.
f1: (i: Int)Either[String,Int]
f2: (i: Int)Either[String,Int]
f3: (i: Int)Either[String,Int]
seq: Seq[Either[String,Int]] = List(Right(1), Right(2), Left(error 3))
scala> seq.collect { case Right(value) => value }
res0: Seq[Int] = List(1, 2)
Functional programming is about expressions and not statements
In
val x = seq.map { f => if (f.isRight) f.right.get }
The below returns Unit
in case f is not right. So, you get Unit
as third value.
if (f.isRight) f.right.get
Functional way is to use have else part also. But In this case, there is no meaningful else case.
Solution 3:[3]
Since Scala 2.13 Either is right biased, so you can simply map to all Right
values with a flatMap:
sequence.flatMap(_.toOption)
In case you need both values, you might also be interested in partitionMap:
val values: Seq[Either[String, String]] = Seq(
Right("Good value 1"),
Left("Bad value 1"),
Right("Good value 2"),
Left("Bad value 2"),
Left("Bad value 3"),
)
// pick both sides in one fell swoop
val (bad, good) = values.partitionMap(identity)
println(s"Good: ${good.mkString(", ")}")
println(s"Bad: ${bad.mkString(", ")}")
/* prints
Good: Good value 1, Good value 2
Bad: Bad value 1, Bad value 2, Bad value 3
*/
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Alex Savitsky |
Solution 2 | |
Solution 3 | Tharabas |