如何将 Either 列表转换为 Right 值列表?
How can I convert a list of Either to a list of Right values?
我对字符串列表进行了一些数据转换,得到了一个 Either 列表,其中 Left 表示错误,Right 表示成功转换的项目。
val results: Seq[Either[String, T]] = ...
我将我的结果划分为:
val (errors, items) = results.partition(_.isLeft)
在进行一些错误处理后,我想 return Seq[T]
个有效项目。这意味着,returning 所有 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}
一一浏览:
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}
这是"it works but why be so complicated?"的另一个例子。在存在 Left 项的情况下,它也不是详尽无遗的,但这永远不会发生,因此无需使用 .collect
.
获得正确值的另一种方法是使用模式匹配:
items.map {
case Right(value) => value
case other => throw new Exception(s"Unexpected Left: $other")
}
但同样,这可能是不必要的,因为您已经知道所有值都是正确的。
如果你要像这样分割results
,我推荐第一个选项,items.map(_.right.get)
。任何其他选项要么具有无法访问的代码(您永远无法通过单元测试或实际操作访问的代码),要么为了 "looking functional".
而不必要地复杂化
考虑使用.get
"code smell":在这种情况下它会起作用,但会使代码的reader暂停并花费一些额外的"cycles" to "prove" 没关系。最好避免在 Either
上使用 .get
,在 Map
或 IndexedSeq
上使用 Option
或 .apply
。
.getOrElse
没问题......但是 null
不是你在 scala 代码中经常看到的东西。同样,让 reader 停下来思考 "why is this here? what will happen if it ends up returning null?" 等。最好也避免。
.asInstanceOf
是……很糟糕。它破坏了类型安全,只是……不是 scala。
剩下 .flatMap(_.toOption)
或 .collect
。两者都很好。我个人更喜欢后者,因为它更明确一点(并且不会让 reader 停下来记住 Either
是有偏见的)。
您也可以使用 foldRight
将分区和提取合二为一 "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)
}
从 Scala 2.13
开始,您可能更喜欢 partitionMap
to partition
。
它根据 returns 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)
这让您可以独立使用左手和右手并使用正确的类型。
我对字符串列表进行了一些数据转换,得到了一个 Either 列表,其中 Left 表示错误,Right 表示成功转换的项目。
val results: Seq[Either[String, T]] = ...
我将我的结果划分为:
val (errors, items) = results.partition(_.isLeft)
在进行一些错误处理后,我想 return Seq[T]
个有效项目。这意味着,returning 所有 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}
一一浏览:
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}
这是"it works but why be so complicated?"的另一个例子。在存在 Left 项的情况下,它也不是详尽无遗的,但这永远不会发生,因此无需使用 .collect
.
获得正确值的另一种方法是使用模式匹配:
items.map {
case Right(value) => value
case other => throw new Exception(s"Unexpected Left: $other")
}
但同样,这可能是不必要的,因为您已经知道所有值都是正确的。
如果你要像这样分割results
,我推荐第一个选项,items.map(_.right.get)
。任何其他选项要么具有无法访问的代码(您永远无法通过单元测试或实际操作访问的代码),要么为了 "looking functional".
考虑使用.get
"code smell":在这种情况下它会起作用,但会使代码的reader暂停并花费一些额外的"cycles" to "prove" 没关系。最好避免在 Either
上使用 .get
,在 Map
或 IndexedSeq
上使用 Option
或 .apply
。
.getOrElse
没问题......但是 null
不是你在 scala 代码中经常看到的东西。同样,让 reader 停下来思考 "why is this here? what will happen if it ends up returning null?" 等。最好也避免。
.asInstanceOf
是……很糟糕。它破坏了类型安全,只是……不是 scala。
剩下 .flatMap(_.toOption)
或 .collect
。两者都很好。我个人更喜欢后者,因为它更明确一点(并且不会让 reader 停下来记住 Either
是有偏见的)。
您也可以使用 foldRight
将分区和提取合二为一 "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)
}
从 Scala 2.13
开始,您可能更喜欢 partitionMap
to partition
。
它根据 returns 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)
这让您可以独立使用左手和右手并使用正确的类型。