0

I would like to convert a List[Box[T]] into a Box[List[T]].

I know that I could use foldRight, but I can't find an elegant way into doing so.

EDIT I would like to keep the properties of Box, that is to say, if there is any failure, return a Box with this failure.

Christopher Chiche
  • 15,075
  • 9
  • 59
  • 98
  • similar [Convert a List of Options to an Option of List using Scalaz](http://stackoverflow.com/questions/2569014/convert-a-list-of-options-to-an-option-of-list-using-scalaz) – 4e6 Mar 15 '12 at 16:49
  • It is in the same mind but you cannot chain options and I would prefer not to use Scalaz. – Christopher Chiche Mar 15 '12 at 17:11

2 Answers2

2

If you only want to collect the "Full" values

I'm not sure why you'd want a Box[List[T]], because the empty list should suffice to signal the lack of any values. I'll assume that's good enough for you.

I don't have a copy of Lift handy, but I know that Box is inspired by Option and has a flatMap method, so:

Long form:

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

Shorter form:

list.flatMap(identity)

Shortest form:

list.flatten

If you want to collect the failures too:

Here's the mapSplit function I use for this kind of problem. You can easily adapt it to use Box instead of 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]())
}

And when I just want to split something that's already a Seq[Either[A,B]], I use this:

/**
 * 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)
Alex Cruise
  • 7,939
  • 1
  • 27
  • 40
  • Hopefully this is what he wants...there's also the "if any are `None`, the list is `None`" version. – Rex Kerr Mar 15 '12 at 16:47
  • That may be, but foldRight isn't typically used for early termination. – Alex Cruise Mar 15 '12 at 16:49
  • @AlexCruise I would like to keep the failures if there are any. That, is, if there is one failure object in the list, I would like to return that one. In your result, the `EmptyBox` objects are ignored I presume. – Christopher Chiche Mar 15 '12 at 17:09
1

It's really easier to do this with a tail-recursive function than with a fold:

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

This works as expected:

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

One can also do this with Iterator.iterate, but it's more awkward and slower, so I would avoid that approach in this case.

(See also my answer in the question 4e6 linked to.)

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407