我对字符串列表进行了一些数据转换,并且得到了Either列表,其中Left表示错误,Right表示成功转换的项目。
val results: Seq[Either[String, T]] = ...
我将结果划分为:
val (errors, items) = results.partition(_.isLeft)
经过一些错误处理后,我想返回Seq[T]
的有效项目。也就是说,返回所有Right元素的值。由于存在分区,我已经知道项Right
的所有元素。我提出了五种可能的方法。但是,在可读性和性能方面最好的是什么?在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}
答案 0 :(得分:2)
使用.get
被认为是“代码异味”:在这种情况下可以使用,但是会使代码阅读者暂停并花费一些额外的“周期”以“证明”它是可以的。最好避免在.get
上使用Either
,在Option
或.apply
上使用Map
或IndexedSeq
之类的东西。
.getOrElse
没问题……但是null
在scala代码中并不常见。再一次,让读者停下来思考:“为什么在这里?如果最后返回null会发生什么?”等。最好避免。
.asInstanceOf
是……很糟糕。它破坏了类型的安全性,而且只是……不是scala。
剩下.flatMap(_.toOption)
或.collect
。两者都很好。我个人更喜欢后者,因为它更为明确(不会使读者停下来记住Either
的偏向)。
您还可以使用foldRight
进行分区和一次“提取”:
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)
}
答案 1 :(得分:0)
一个个地经历它们:
items.map(_.right.get)
您已经知道这些都是权利。这绝对没问题。
items.map(_.right.getOrElse(null))
.getOrElse
在这里是不必要的,因为您已经知道它永远不会发生。我建议您在发现一个Left(某种程度上)的情况下抛出异常,例如:items.map(x => x.right.getOrElse(throw new Exception(s"Unexpected Left: [$x]"))
(或您认为合适的任何异常),而不要干预null
值。
items.map(_.asInstanceOf[Right[String, T]].value)
这不必要地复杂。我也无法对此进行编译,但是我可能做错了什么。无论哪种方式,都不需要在这里使用asInstanceOf
。
items.flatMap(_.toOption)
我也无法编译它。 items.flatMap(_.right.toOption)
为我编译,但到那时它始终是Some,而您仍然必须.get
。
items.collect{case Right(item) => item}
这是“有效,但为什么要这么复杂?”的另一种情况。对于存在Left项的情况,它也不是详尽无遗的,但这绝不应该发生,因此不需要使用.collect
。
您可以获得正确值的另一种方法是使用模式匹配:
items.map {
case Right(value) => value
case other => throw new Exception(s"Unexpected Left: $other")
}
但是同样,您可能已经没有必要了,因为您已经知道所有值都是正确的。
如果要像这样对results
进行分区,建议您选择第一个选项items.map(_.right.get)
。其他任何选项要么具有无法访问的代码(您将永远无法通过单元测试或实际操作获得的代码),要么由于“看上去很实用”而变得不必要地复杂。
答案 2 :(得分:0)
从Scala 2.13
开始,您可能更喜欢partitionMap
比partition
。
它基于返回Right
或Left
的函数对元素进行分区。您的情况就是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)
使您可以单独使用权利和权利来使用权利。