如何将Either列表转换为Right值列表?

时间:2018-11-07 13:13:15

标签: scala

我对字符串列表进行了一些数据转换,并且得到了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}

3 个答案:

答案 0 :(得分:2)

使用.get被认为是“代码异味”:在这种情况下可以使用,但是会使代码阅读者暂停并花费一些额外的“周期”以“证明”它是可以的。最好避免在.get上使用Either,在Option.apply上使用MapIndexedSeq之类的东西。

.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开始,您可能更喜欢partitionMappartition

它基于返回RightLeft的函数对元素进行分区。您的情况就是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)

使您可以单独使用权利和权利来使用权利。