Объединение нескольких списков произвольной длины
Я ищу подход для объединения нескольких списков следующим образом:
ListA a b c
ListB 1 2 3 4
ListC + # * § %
..
..
..
Resulting List: a 1 + b 2 # c 3 * 4 § %
В словах: элементы в последовательном порядке, начиная с первого списка, объединенного в результирующий список. Любое количество входных списков может иметь различную длину.
Я использовал несколько подходов с вариантами застежки-молнии, скользящими итераторами, но никто не работал и особенно заботился о разных длинах списков. Должен быть элегантный способ в scala;)
Ответы
Ответ 1
val lists = List(ListA, ListB, ListC)
lists.flatMap(_.zipWithIndex).sortBy(_._2).map(_._1)
Это довольно понятно. Он просто фиксирует каждое значение своей позицией в соответствующем списке, сортирует по индексу, затем вытаскивает значения обратно.
Ответ 2
Вот как бы я это сделал:
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(Some(_)) ++ 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)
}
}
Ответ 3
Здесь небольшое рекурсивное решение. Покажет потоковый подход позже...
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
}
Я уверен, что Луиджи мог бы сжать это с одним лайнером;) У меня это примерно так же мало, как я могу.
Ответ 4
Здесь важное требование, если эффективность имеет первостепенное значение:
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
}
Ответ 5
Вы можете использовать padTo
, transpose
и flatten
здесь:
lists.map(_.map(Some(_)).padTo(lists.map(_.length).max, None)).transpose.flatten.flatten
Ответ 6
Здесь что-то короткое, но не очень эффективное:
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
Ответ 7
Здесь рекурсивное решение, что O (n). Принятым решением (с использованием сортировки) является O (nlog (n)). Некоторое тестирование, которое я сделал, предполагает, что второе решение, использующее transpose, также O (nlog (n)) из-за реализации transpose. Использование реверса ниже выглядит подозрительно (так как это сама операция 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, zss) => intercalateHelper(zss.reverse, Nil, merged)
case (Nil::xss, zss) => intercalateHelper(xss, zss, merged)
case ( (y::ys)::xss, zss) => intercalateHelper(xss, ys::zss, y::merged)
}
}
intercalateHelper(lists, List.empty, List.empty).reverse
}