【问题标题】:How to split a List[Either[A, B]]如何拆分列表[Either[A, B]]
【发布时间】:2014-10-26 18:38:45
【问题描述】:

我想将List[Either[A, B]] 拆分为两个列表。

有没有更好的办法?

def lefts[A, B](eithers : List[Either[A, B]]) : List[A] = eithers.collect { case Left(l) => l}
def rights[A, B](eithers : List[Either[A, B]]) : List[B] = eithers.collect { case Right(r) => r}

【问题讨论】:

    标签: scala either


    【解决方案1】:

    Scala 2.13 开始,现在大多数集合都提供了partitionMap 方法,该方法基于返回RightLeft 的函数对元素进行分区。

    在我们的例子中,我们甚至不需要将输入转换为RightLeft 的函数来定义分区,因为我们已经有了Rights 和Lefts。因此简单使用identity

    val (lefts, rights) = List(Right(2), Left("a"), Left("b")).partitionMap(identity)
    // lefts: List[String] = List(a, b)
    // rights: List[Int] = List(2)
    

    【讨论】:

      【解决方案2】:

      不确定这是否真的更整洁,但是:

      scala> def splitEitherList[A,B](el: List[Either[A,B]]) = {
               val (lefts, rights) = el.partition(_.isLeft)
               (lefts.map(_.left.get), rights.map(_.right.get))
             }
      splitEitherList: [A, B](el: List[Either[A,B]])(List[A], List[B])
      
      scala> val el : List[Either[Int, String]] = List(Left(1), Right("Success"), Left(42))
      el: List[Either[Int,String]] = List(Left(1), Right(Success), Left(42))
      
      scala> val (leftValues, rightValues) = splitEitherList(el)
      leftValues: List[Int] = List(1, 42)
      rightValues: List[String] = List("Success")
      

      【讨论】:

      • 编辑 : (lefts.map(.left.get), rights.map(.right.get)) 变为 (lefts.flatMap(.left. toOption), rights.flatMap(.right.toOption))
      【解决方案3】:

      如果scalaz 是您的依赖项之一,我会简单地使用separate

      import scalaz.std.list._
      import scalaz.std.either._
      import scalaz.syntax.monadPlus._
      
      val el : List[Either[Int, String]] = List(Left(1), Right("Success"), Left(42))
      
      scala> val (lefts, rights) = el.separate
      lefts: List[Int] = List(1, 42)
      rights: List[String] = List(Success)
      

      【讨论】:

      【解决方案4】:

      你可以这样做:

      val (lefts, rights) = eithers.foldRight((List[Int](), List[String]()))((e, p) => e.fold(l => (l :: p._1, p._2), r => (p._1, r :: p._2)))
      

      【讨论】:

        【解决方案5】:

        一个紧凑但 CPU 效率不高的解决方案:

        val lefts = list.flatMap(_.left.toOption)
        val rights = list.flatMap(_.right.toOption)
        

        【讨论】:

          【解决方案6】:

          好吧,如果它不必是单线……那么它可以是一个明智的选择。

          def split[A,B](eithers : List[Either[A, B]]):(List[A],List[B]) = {
            val lefts = scala.collection.mutable.ListBuffer[A]()
            val rights = scala.collection.mutable.ListBuffer[B]()
            eithers.map {
              case Left(l) => lefts += l
              case Right(r) => rights += r
            }
            (lefts.toList, rights.toList)
          }
          

          但是,老实说,我更喜欢 Marth 的回答 :)

          【讨论】:

            【解决方案7】:

            如果您打算像 Marth 的回答那样费心抽象功能,那么使用 roterl 的解决方案实际上可能更有意义:

            def splitEitherList[A,B](el: List[Either[A,B]]): (List[A], List[B]) =
              (el :\ (List[A](), List[B]()))((e, p) =>
                e.fold(l => (l :: p._1, p._2), r => (p._1, r :: p._2)))
            
            val x = List(Left(1), Right(3), Left(2), Left(4), Right(8))
            splitEitherList(x) // (List(1, 2, 4), List(3, 8))
            

            这种方式奖励更多功能性的布朗尼积分,但也可能更高效,因为它利用正确的折叠来一次性创建列表

            但是,如果您是在运行中进行操作和/或发现折叠难以阅读,那么请务必

            el.partition(_.isLeft) match { case (lefts, rights) =>
              (lefts.map(_.left.get), rights.map(_.right.get)) }
            

            【讨论】:

              【解决方案8】:

              Seq 的一些实用解决方案。

              def partition[A, B](seq: Seq[Either[A, B]]): (Seq[A], Seq[B]) = {
                seq.foldLeft[(Seq[A], Seq[B])]((Nil, Nil)) { case ((ls, rs), next) =>
                  next match {
                    case Left(l) => (ls :+ l, rs)
                    case Right(r) => (ls, rs :+ r)
                  }
                }
              }
              

              【讨论】:

                猜你喜欢
                • 2011-11-06
                • 2015-12-08
                • 1970-01-01
                • 1970-01-01
                • 2012-10-18
                • 2019-10-23
                • 1970-01-01
                • 2016-10-27
                • 1970-01-01
                相关资源
                最近更新 更多