Efficient and/or idiomatic way to turn Seq[Either[String, Int]] to (Seq[String], Seq[Int])

余生长醉 提交于 2019-12-12 17:32:14

问题


Slightly simplifying, my problem comes from a list of strings input that I want to parse with a function parse returning Either[String,Int].

Then list.map(parse) returns a list of Eithers. The next step in the program is to format an error message summing up all the errors or passing on the list of parsed integers.

Lets call the solution I'm looking for partitionEithers.

Calling

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

Would give

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

Finding something like this in the standard library would be best. Failing that some kind of clean, idiomatic and efficient solution would be best. Also some kind of efficient utility function I could just paste into my projects would be ok.

I was very confused between these 3 earlier questions. As far as I can tell, neither of those questions matches my case, but some answers there seem to contain valid answers to this question.


回答1:


I don't really get the amount of contortions of the other answers. So here is a one liner:

scala> val es:List[Either[Int,String]] = 
           List(Left(1),Left(2),Right("A"),Right("B"),Left(3),Right("C"))
es: List[Either[Int,String]] = List(Left(1), Left(2), Right(A), Right(B), Left(3), Right(C))

scala> es.foldRight( (List[Int](), List[String]()) ) { 
         case ( e, (ls, rs) ) => e.fold( l => ( l :: ls, rs), r => ( ls, r :: rs ) ) 
       }
res5: (List[Int], List[String]) = (List(1, 2, 3),List(A, B, C))



回答2:


Scala collections offer a partition function:

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))

UPDATE

If you want to wrap it in a (maybe even somewhat type safer) generic function:

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 })
}



回答3:


You could use separate from MonadPlus (scalaz) or MonadCombine (cats) :

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:


Here is an imperative implementation mimicking the style of Scala collection internals.

I wonder if there should something like this in there, since at least I run into this from time to time.

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))



回答5:


You can use 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))


来源:https://stackoverflow.com/questions/36553547/efficient-and-or-idiomatic-way-to-turn-seqeitherstring-int-to-seqstring

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!