Liftweb - 将列表[Box [T]]转换为Box [List [T]]

时间:2012-03-15 16:40:18

标签: scala lift scala-collections

我想将List[Box[T]]转换为Box[List[T]]

我知道我可以使用foldRight,但我找不到优雅的方法。

编辑我想保留Box的属性,也就是说,如果有任何失败,请返回Box此失败。

2 个答案:

答案 0 :(得分:2)

如果您只想收集“完整”值

我不确定你为什么想要一个Box [List [T]],因为空列表应该足以表示缺少任何值。我认为这对你来说已经足够了。

我没有Lift方便的副本,但我知道Box的灵感来自Option并且有一个flatMap方法,所以:

长篇:

for {
  box <- list
  value <- box
} yield value

缩短形式:

list.flatMap(identity)

最短格式:

list.flatten

如果您也想收集失败:

这是我用于此类问题的mapSplit函数。您可以轻松地对其进行调整,以使用Box代替Either

/**
 * Splits the input list into a list of B's and a list of C's, depending on which type of value the mapper function returns.
 */
def mapSplit[A,B,C](in: Traversable[A])(mapper: (A) ⇒ Either[B,C]): (Seq[B], Seq[C]) = {
  @tailrec
  def mapSplit0(in: Traversable[A], bs: Vector[B], cs: Vector[C]): (Seq[B], Seq[C]) = {
    in match {
      case t if t.nonEmpty ⇒
        val a = t.head
        val as = t.tail
        mapper(a) match {
          case Left(b)  ⇒ mapSplit0(as, bs :+ b, cs     )
          case Right(c) ⇒ mapSplit0(as, bs,      cs :+ c)
        }
      case t ⇒
        (bs, cs)
    }
  }

  mapSplit0(in, Vector[B](), Vector[C]())
}

当我只想拆分已经是Seq [Aither [A,B]]的东西时,我会用它:

/**
 * Splits a List[Either[A,B]] into a List[A] from the lefts and a List[B] from the   rights.
 * A degenerate form of {@link #mapSplit}.
 */
def splitEither[A,B](in: Traversable[Either[A,B]]): (Seq[A], Seq[B]) = mapSplit(in)(identity)

答案 1 :(得分:1)

使用尾递归函数比使用折叠更容易做到这一点:

final def flip[T](l: List[Option[T]], found: List[T] = Nil): Option[List[T]] = l match {
  case Nil => if (found.isEmpty) None else Some(found.reverse)
  case None :: rest => None
  case Some(x) :: rest => flip(rest, x :: found)
}

这可以按预期工作:

scala> flip(List(Some(3),Some(5),Some(2)))
res3: Option[List[Int]] = Some(List(3, 5, 2))

scala> flip(List(Some(1),None,Some(-1)))
res4: Option[List[Int]] = None

也可以用Iterator.iterate执行此操作,但它更笨拙和更慢,所以在这种情况下我会避免这种方法。

(另请参阅我与4e6相关的答案。)