I do some data conversion on a list of string and I get a list of Either where Left represents an error and Right represents a successfully converted item.
val results: Seq[Either[String, T]] = ...
I partition my results with:
val (errors, items) = results.partition(_.isLeft)
After doing some error processing I want to return a Seq[T]
of valid items. That means, returning the value of all Right elements. Because of the partitioning I already knew that all elements of items Right
. I have come up with five possibilities of how to do it. But what is the best in readability and performance? Is there an idiomatic way of how to do it in Scala?
// which variant is most scala like and still understandable?
items.map(_.right.get)
items.map(_.right.getOrElse(null))
items.map(_.asInstanceOf[Right[String, T]].value)
items.flatMap(_.toOption)
items.collect{case Right(item) => item}
Using .get
is considered "code smell": it will work in this case, but makes the reader of the code pause and spend a few extra "cycles" in order to "prove" that it is ok. It is better to avoid using things like .get
on Either
and Option
or .apply
on a Map
or a IndexedSeq
.
.getOrElse
is ok ... but null
is not something you often see in scala code. Again, makes the reader stop and think "why is this here? what will happen if it ends up returning null?" etc. Better to avoid as well.
.asInstanceOf
is ... just bad. It breaks type safety, and is just ... not scala.
That leaves .flatMap(_.toOption)
or .collect
. Both are fine. I would personally prefer the latter as it is a bit more explicit (and does not make the reader stop to remember which way Either
is biased).
You could also use foldRight
to do both partition and extract in one "go":
val (errors, items) = results.foldRight[(List[String], List[T])](Nil,Nil) {
case (Left(error), (e, i)) => (error :: e, i)
case ((Right(result), (e, i)) => (e, result :: i)
}
Starting in Scala 2.13
, you'll probably prefer partitionMap
to partition
.
It partitions elements based on a function which returns either Right
or Left
. Which in your case, is simply the identity
:
val (lefts, rights) = List(Right(1), Left("2"), Left("3")).partitionMap(identity)
// val lefts: List[String] = List(2, 3)
// val rights: List[Int] = List(1)
which let you use lefts and rights independently and with the right types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With