Is there a safe way in Scala to transpose a List of unequal-length Lists?

后端 未结 5 956
南方客
南方客 2020-12-03 11:37

Given the following List:

val l = List(List(1, 2, 3), List(4, 5), List(6, 7, 8))

If I try to transpose it, Scala will throw the following e

相关标签:
5条回答
  • 2020-12-03 12:18

    This is probably the cleanest:

    def transpose[T](l: List[List[T]]): List[List[T]] =
         l.flatMap(_.headOption) match {
             case Nil => Nil
             case head => head :: transpose(l.map(_.drop(1)))
         }
    

    or a modified version that is even more efficient:

    def transpose[T](l: List[List[T]]): List[List[T]] =
         l.flatMap(_.headOption) match {
             case Nil => Nil
             case head => head :: transpose(l.collect { case _ :: tail => tail })
         }
    
    0 讨论(0)
  • 2020-12-03 12:20

    How about this:

        scala> def transpose[A](xs: List[List[A]]): List[List[A]] = xs.filter(_.nonEmpty) match {    
             |     case Nil    =>  Nil
             |     case ys: List[List[A]] => ys.map{ _.head }::transpose(ys.map{ _.tail })
             | }
        warning: there were unchecked warnings; re-run with -unchecked for details
        transpose: [A](xs: List[List[A]])List[List[A]]
    
        scala> val ls = List(List(1, 2, 3), List(4, 5), List(6, 7, 8))
        ls: List[List[Int]] = List(List(1, 2, 3), List(4, 5), List(6, 7, 8))
    
        scala> transpose(ls)
        res0: List[List[Int]] = List(List(1, 4, 6), List(2, 5, 7), List(3, 8))
    
        scala> val xs = List(List(1,2,3), List(4,5,99,100), List(6,7,8))
    xs: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 99, 100), List(6, 7, 8))
    
    scala> transpose(xs)
    res1: List[List[Int]] = List(List(1, 4, 6), List(2, 5, 7), List(3, 99, 8), List(100))
    
    0 讨论(0)
  • 2020-12-03 12:20

    How about this one-liner using the Scala's standard Api:

    ((l map (_.toArray)) toArray).transpose map (_.toList) toList
    

    This gets the job done and is O(N*M), where N is the length of the wrapper list and M is the length of the longest list inside the wrapper list.

    0 讨论(0)
  • 2020-12-03 12:30

    I suspect the reason transpose is not defined on a "non-rectangular" list of lists is because mathematically the transpose operation is well-defined only on "rectangular structures". A desirable property of a transpose operation is that transpose( transpose(x) ) == x. This is not the case in your generalization of the transpose operation on non-rectangular list of lists.

    Also, take a look at my post on Transposing arbitrary collection-of-collections in Scala and think about doing it for non-rectangular collections-of-collections. You will end up with mathematically inconsistent definitions, leave alone implementations.

    I do agree that idiosyncratic "transpose" operations are often useful, but I also think that they should not be made available in standard libraries because of potential confusion regarding their precise definitions.

    0 讨论(0)
  • 2020-12-03 12:30

    I don't know of (and can't imagine - isn't this is a bit odd?! [see discussion in comments]) a library function, but I can polish the code a little:

    scala> def transpose(x: List[List[Int]]): List[List[Int]] = {
         |   val b = new ListBuffer[List[Int]]
         |   var y = x filter (!_.isEmpty)
         |   while (!y.isEmpty) {
         |     b += y map (_.head)
         |     y = y map (_.tail) filter (!_.isEmpty)
         |   }
         |   b.toList
         | }
    
    0 讨论(0)
提交回复
热议问题