组合多个任意长度的列表

时间:2013-09-30 14:41:08

标签: scala scala-collections

我正在寻找以下列方式加入多个列表的方法:

ListA a b c
ListB 1 2 3 4
ListC + # * § %
..
..
..

Resulting List: a 1 + b 2 # c 3 * 4 § %

单词:按顺序排列的元素,从第一个列表开始合并到结果列表中。任意数量的输入列表的长度可能不同。

我使用了zip的变体,滑动迭代器的多种方法但没有工作,特别是处理不同的列表长度。在scala中必须有一种优雅的方式;)

7 个答案:

答案 0 :(得分:20)

val lists = List(ListA, ListB, ListC)

lists.flatMap(_.zipWithIndex).sortBy(_._2).map(_._1)

这是不言自明的。它只是将每个值与其各自列表中的位置进行拉链,按索引排序,然后将值拉回。

答案 1 :(得分:5)

我将如何做到这一点:

class ListTests extends FunSuite {
  test("The three lists from his example") {
    val l1 = List("a", "b", "c")
    val l2 = List(1, 2, 3, 4)
    val l3 = List("+", "#", "*", "§", "%")

    // All lists together
    val l = List(l1, l2, l3)

    // Max length of a list (to pad the shorter ones)
    val maxLen = l.map(_.size).max

    // Wrap the elements in Option and pad with None
    val padded = l.map { list => list.map(Option(_)) ++ Stream.continually(None).take(maxLen - list.size) }

    // Transpose 
    val trans = padded.transpose

    // Flatten the lists then flatten the options
    val result = trans.flatten.flatten

    // Viola 
    assert(List("a", 1, "+", "b", 2, "#", "c", 3, "*", 4, "§", "%") === result)
  }
}

答案 2 :(得分:1)

这是一个小的递归解决方案。稍后会显示一个流方法...

def flatList(lists: List[List[Any]]) = {
  def loop(output: List[Any], xss: List[List[Any]]): List[Any] = (xss collect { case x :: xs => x }) match {
    case Nil => output
    case heads => loop(output ::: heads, xss.collect({ case x :: xs => xs })) 
  }
  loop(List[Any](), lists)
}

这是一个简单的流方法,可以处理任意序列的序列,每个序列都有无穷长。

def flatSeqs[A](ssa: Seq[Seq[A]]): Stream[A] = {
  def seqs(xss: Seq[Seq[A]]): Stream[Seq[A]] = xss collect { case xs if !xs.isEmpty => xs } match {
    case Nil => Stream.empty
    case heads => heads #:: seqs(xss collect { case xs if !xs.isEmpty => xs.tail })
  }
  seqs(ssa).flatten
}

我确信Luigi可以把它缩小成一个单行;)我有它的尽可能小。

答案 3 :(得分:1)

如果效率至关重要,那么这是一个必不可少的解决方案:

def combine[T](xss: List[List[T]]): List[T] = {
  val b = List.newBuilder[T]
  var its = xss.map(_.iterator)
  while (!its.isEmpty) {
    its = its.filter(_.hasNext)
    its.foreach(b += _.next)
  }
  b.result
}

答案 4 :(得分:1)

您可以在此处使用padTotransposeflatten,效果良好:

lists.map(_.map(Some(_)).padTo(lists.map(_.length).max, None)).transpose.flatten.flatten

答案 5 :(得分:0)

这里有一些简短但效率不高的东西:

def heads[A](xss: List[List[A]]) = xss.map(_.splitAt(1)).unzip
def interleave[A](xss: List[List[A]]) = Iterator.
  iterate(heads(xss)){ case (_, tails) => heads(tails) }.
  map(_._1.flatten).
  takeWhile(! _.isEmpty).
  flatten.toList

答案 6 :(得分:0)

这是O(n)的递归解决方案。接受的解决方案(使用排序)是O(nlog(n))。我所做的一些测试表明,由于实现了转置,因此第二种使用转置的解决方案也是 O(nlog(n))。下面使用反向控件看起来很可疑(因为它本身是O(n)操作),但请使自己确信,它不能被调用得太频繁或在太大的列表上被调用。

def intercalate[T](lists: List[List[T]]) : List[T] = {
    def intercalateHelper(newLists: List[List[T]], oldLists: List[List[T]], merged: List[T]): List[T] = {
      (newLists, oldLists) match {
        case (Nil, Nil) => merged
        case (Nil, z) => intercalateHelper(z.reverse, Nil, merged)
        case (Nil::xs, z) => intercalateHelper(xs, z, merged)
        case ( (y::ys)::xs, z) => intercalateHelper(xs, ys::z, y::merged)
      }
    }
    intercalateHelper(lists, List.empty, List.empty).reverse
  }