将Seq [[String,Int]]转换为(Seq [String],Seq [Int])的高效和/或惯用方法

时间:2016-04-11 15:54:27

标签: scala either

稍微简化一下,我的问题来自一个字符串input列表,我希望使用返回parse的函数Either[String,Int]进行解析。

然后list.map(parse)会返回Either的列表。该程序的下一步是格式化错误消息,总结传递在已解析整数列表中的所有错误

让我们调用我正在寻找的解决方案partitionEithers

调用

partitionEithers(List(Left("foo"), Right(1), Left("bar")))

会给予

(List("foo", "bar"),List(1))

在标准库中找到这样的东西是最好的。如果没有某种清洁,惯用和有效的解决方案,那将是最好的。还有一些高效的实用功能我可以粘贴到我的项目中就行了。

我在these 3 earlier questions之间感到非常困惑。据我所知,这些问题都不符合我的情况,但有些答案似乎包含了这个问题的有效答案。

5 个答案:

答案 0 :(得分:6)

Scala集合提供partition函数:

val eithers: List[Either[String, Int]] = List(Left("foo"), Right(1), Left("bar"))

eithers.partition(_.isLeft) match {
  case (leftList, rightList) =>
    (leftList.map(_.left.get), rightList.map(_.right.get))
}

=> res0: (List[String], List[Int]) = (List(foo, bar),List(1))

<强>更新

如果你想把它包装成一个(甚至有点类型更安全)的通用函数:

def partitionEither[Left : ClassTag, Right : ClassTag](in: List[Either[Left, Right]]): (List[Left], List[Right]) =
  in.partition(_.isLeft) match {
    case (leftList, rightList) =>
      (leftList.collect { case Left(l: Left) => l }, rightList.collect { case Right(r: Right) => r })
}

答案 1 :(得分:4)

我并没有真正得到其他答案的扭曲量。所以这是一个班轮:

datediff()

答案 2 :(得分:3)

这是一个模仿Scala集合内部风格的命令式实现。

我想知道那里是否应该有这样的东西,因为至少我会不时遇到这种情况。

import collection._
import generic._
def partitionEithers[L, R, E, I, CL, CR]
                    (lrs: I)
                    (implicit evI: I <:< GenTraversableOnce[E],
                              evE: E <:< Either[L, R],
                              cbfl: CanBuildFrom[I, L, CL],
                              cbfr: CanBuildFrom[I, R, CR])
                    : (CL, CR) = {
  val ls = cbfl()
  val rs = cbfr()

  ls.sizeHint(lrs.size)
  rs.sizeHint(lrs.size)

  lrs.foreach { e => evE(e) match {
    case Left(l)  => ls += l
    case Right(r) => rs += r
  } }

  (ls.result(), rs.result())
}

partitionEithers(List(Left("foo"), Right(1), Left("bar"))) == (List("foo", "bar"), List(1))
partitionEithers(Set(Left("foo"), Right(1), Left("bar"), Right(1))) == (Set("foo", "bar"), Set(1))

答案 3 :(得分:3)

您可以使用separate(scalaz)或MonadPlus(猫)中的MonadCombine

import scala.util.{Either, Left, Right}

import scalaz.std.list._
import scalaz.std.either._
import scalaz.syntax.monadPlus._

val l: List[Either[String, Int]] = List(Right(1), Left("error"), Right(2))
l.separate  
// (List[String], List[Int]) = (List(error),List(1, 2))

答案 4 :(得分:0)

您可以使用foldLeft。

  def f(s: Seq[Either[String, Int]]): (Seq[String], Seq[Int]) = {
    s.foldRight((Seq[String](), Seq[Int]())) { case (c, r) =>
      c match {
        case Left(le) => (le +: r._1, r._2)
        case Right(ri) => (r._1 , ri +: r._2)
      }
    }
  }

val eithers: List[Either[String, Int]] = List(Left("foo"), Right(1), Left("bar"))

scala> f(eithers)
res0: (Seq[String], Seq[Int]) = (List(foo, bar),List(1))