2013-09-30 10 views
14

szukam podejścia do przyłączenia wielu list w następujący sposób:łączenie list o dowolnej długości

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

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

słownie elementów w kolejności, poczynając od pierwszej listy połączone w otrzymanej listy. Dowolna ilość list wejściowych może mieć różną długość.

Użyłem wielu podejść z wariantami zip, przesuwając iteratory, ale żaden nie działał, a szczególnie zadbał o różne długości list. Musi być elegancki sposób w Scala;)

+2

pocztowy jest naturalnym dla tego produktu. Czego próbowałeś, to nie działało? – itsbruce

+0

Zip pozwala mi łączyć 2 listy. posiadanie wielu list przynajmniej komplikuje jego użycie dla mnie (początkujący w scala), nie pozwala też na łączenie list o braku pasującej długości. Po zakończeniu listowania kolejne elementy z innych list nie są spakowane. –

+5

@itsbruce: To wcale nie jest banalne z 'zip', a nawet z' zipAll', 'zipWith', itp. Byłoby to trochę trudne. –

Odpowiedz

19
val lists = List(ListA, ListB, ListC) 

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

To dość oczywiste. Po prostu zamienia każdą wartość z pozycją na odpowiedniej liście, sortuje według indeksu, a następnie wycofuje wartości z powrotem.

+0

Zawsze czuję, że muszę się skoncentrować, zanim Luigi się tu dostanie ... – itsbruce

+1

Właśnie zostałem właścicielem. – joescii

+1

Czy gwarantuje się stabilność? –

5

Oto jak zrobiłbym to:

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

Możesz być zainteresowany wbudowaną metodą 'padTo', która pozwala upraszczaj: 'val padded = l.map (_. map (Option (_)). padTo (maxLen, None))' –

+0

Bardzo fajnie, @LuigiPlinge. Dzięki za wskazówkę! – joescii

1

Oto mały rozwiązanie rekurencyjne. Pokaże podejście strumieni później ...

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

I tu jest podejście proste strumienie, które mogą poradzić sobie z dowolnej kolejności sekwencji, każda potencjalnie nieskończonej długości.

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 
} 

Jestem pewien, że Luigi mógł zmniejszyć to do jednego liniowca;) Mam go tak mały, jak mogę.

0

Oto coś krótki, ale nie nadzwyczaj skuteczny:

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 
1

Oto imperatywem rozwiązanie, jeśli wydajność jest najważniejsza:

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

Można użyć padTo, transpose i flatten z dobrym skutkiem tutaj:

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