Kann ich in Scala mehr als zwei Listen zusammen komprimieren?

92

Angesichts der folgenden Scala-Liste:

val l = List(List("a1", "b1", "c1"), List("a2", "b2", "c2"), List("a3", "b3", "c3"))

Wie bekomme ich:

List(("a1", "a2", "a3"), ("b1", "b2", "b3"), ("c1", "c2", "c3"))

Da zip nur zum Kombinieren von zwei Listen verwendet werden kann, müssten Sie die Hauptliste irgendwie iterieren / reduzieren. Es überrascht nicht, dass Folgendes nicht funktioniert:

scala> l reduceLeft ((a, b) => a zip b)
<console>:6: error: type mismatch;
 found   : List[(String, String)]
 required: List[String]
       l reduceLeft ((a, b) => a zip b)

Irgendwelche Vorschläge, wie man das macht? Ich glaube, ich vermisse einen sehr einfachen Weg, dies zu tun.

Update: Ich suche nach einer Lösung, die eine Liste von N Listen mit jeweils M Elementen erstellt und eine Liste von M TupelNs erstellt.

Update 2: Wie sich herausstellt, ist es für meinen speziellen Anwendungsfall besser, eine Liste mit Listen zu haben, als eine Liste mit Tupeln. Daher akzeptiere ich die Antwort von Kürbis. Es ist auch das einfachste, da es eine native Methode verwendet.

pr1001
quelle
Mögliches Duplikat von Zip-Mehrfachsequenzen
Suma
Auf jeden Fall erwähnenswert: stackoverflow.com/questions/1683312/…
Venkat Sudheer Reddy Aedama
@ VenkatSudheerReddyAedama Auch von mir gefragt, fünf Tage später. ;-)
pr1001

Antworten:

36

Ich glaube nicht, dass es möglich ist, eine Liste von Tupeln beliebiger Größe zu erstellen, aber die Transponierungsfunktion macht genau das, was Sie brauchen, wenn es Ihnen nichts ausmacht, stattdessen eine Liste von Listen zu erhalten.

Copumpkin
quelle
Danke, das funktioniert perfekt! Wenn ich auf meinen speziellen Anwendungsfall eingehe, sehe ich, dass eine Liste von Listen sowieso besser wäre, da ich die verschiedenen Unterlisten zuordnen und reduzieren muss.
pr1001
2
@ JoshCason im engsten Sinne von "mehr als zwei", klar. Drei ist in der Tat mehr als zwei. Ich interpretierte die Frage im weiteren Sinne von "mehr als zwei", was willkürlich viele bedeutet. Und in diesem Fall ist es nicht möglich, das zu tun, was die Frage will, es sei denn, Sie greifen nach HLists und dergleichen.
Copumpkin
Der Link in der Antwort ist defekt, neuer Link ist scala-lang.org/api/2.12.1/scala/…
Ramesh Maharjan
213
scala> (List(1,2,3),List(4,5,6),List(7,8,9)).zipped.toList
res0: List[(Int, Int, Int)] = List((1,4,7), (2,5,8), (3,6,9))

Zum späteren Nachschlagen.

Xorlev
quelle
32
Dies ist ideal zum Komprimieren von drei Listen. Schade, dass dies nicht für mehr als drei Listen
funktioniert
2
Beachten Sie, dass dies zuerst in einem Tupel sein muss: zippedist keine Funktion von List.
Nathaniel Ford
6
zippedist in Scala 2.13 veraltet. in 2.13, dol1.lazyZip(l2).lazyZip(l3).toList
Seth Tisue
30

Dieser Code wird also nicht die Anforderungen des OP erfüllen, und zwar nicht nur, weil dies ein vier Jahre alter Thread ist, sondern er beantwortet auch die Titelfrage, und vielleicht findet ihn sogar jemand nützlich.

So komprimieren Sie 3 Sammlungen:

as zip bs zip cs map { 
  case ((a,b), c) => (a,b,c)
}
Bijou Trouvaille
quelle
4 Sammlungen zu machen sieht aus wie:as zip bs zip cs zip ds map { case ((a,b),c)} map {case ((a,b),c,d)=>(a,b,c,d)}
James Tobin
1
@ JamesTobin, du verkürzst aufas zip bs zip cs zip ds map {case (((a,b),c),d)=>(a,b,c,d) }
Keepscoding
Schön für Listen unterschiedlichen Typs.
FP frei
11

Ja, mit zip3 .

Harold L.
quelle
2
Danke, aber es funktioniert nur mit 3 Listen. Ich suche nach einer Lösung, die eine Liste von N Listen mit jeweils M Elementen erstellt und eine Liste von M TupelnNs erstellt.
pr1001
6

transposemacht den Trick. Ein möglicher Algorithmus ist:

def combineLists[A](ss:List[A]*) = {
    val sa = ss.reverse;
    (sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1))
}

Beispielsweise:

combineLists(List(1, 2, 3), List(10,20), List(100, 200, 300))
// => List[List[Int]] = List(List(1, 10, 100), List(2, 20, 200))

Die Antwort wird auf die Größe der kürzesten Liste in der Eingabe gekürzt.

combineLists(List(1, 2, 3), List(10,20))
// => List[List[Int]] = List(List(1, 10), List(2, 20))
WP McNeill
quelle
1
Diese Antwort reicht fast aus, kehrt jedoch die Elemente um. Können Sie eine verbesserte Version vorschlagen, die die Ausgabe in der erwarteten Reihenfolge erzeugt? danke
fracca
Geänderte Version, die die Reihenfolge beibehält: def combineLists[A](ss:List[A]*) = { val sa = ss.reverse; (sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1)) }
Rogermenezes
5

Scala behandelt alle seine verschiedenen Tupel Größen wie verschiedene Klassen ( Tuple1, Tuple2, Tuple3, Tuple4, ..., Tuple22) , während sie tun alles erben von derProduct Merkmal, das Merkmal tragen nicht genügend Informationen , um tatsächlich die Datenwerte aus den verschiedenen Größen von Tupeln verwenden wenn sie alle von derselben Funktion zurückgegeben werden könnten. (Und Scalas Generika sind auch nicht leistungsfähig genug, um diesen Fall zu behandeln.)

Am besten schreiben Sie Überladungen der Zip-Funktion für alle 22 Tupelgrößen. Ein Codegenerator würde Ihnen wahrscheinlich dabei helfen.

Ken Bloom
quelle
5

Wenn Sie nicht den Weg des anwendbaren Scalaz / Katzen / (fügen Sie hier Ihre bevorzugte funktionale Bibliothek ein) gehen möchten, ist der Mustervergleich der richtige Weg, obwohl die (_, _)Syntax beim Verschachteln etwas umständlich ist. Ändern wir ihn also:

import scala.{Tuple2 => &}

for (i1 & i2 & i3 & i4 <- list1 zip list2 zip list3 zip list4) yield (i1, i2, i3, i4)

Das &ist hier eine willkürliche Wahl, alles was gut aussieht, sollte es tun. Bei der Codeüberprüfung werden Sie wahrscheinlich ein paar hochgezogene Augenbrauen bekommen.

Es sollte auch mit allem funktionieren, was Sie können zip(z. B. Futures)

L4Z
quelle
5

Ich glaube nicht, dass das möglich ist, ohne sich zu wiederholen. Aus einem einfachen Grund: Sie können den Rückgabetyp der gewünschten Funktion nicht definieren.

Wenn Ihre Eingabe beispielsweise wäre, wäre List(List(1,2), List(3,4))der Rückgabetyp List[Tuple2[Int]]. Wenn es drei Elemente hätte, wäre der RückgabetypList[Tuple3[Int]] und so weiter.

Sie könnten zurückkehren List[AnyRef]oder sogar List[Product]eine Reihe von Fällen erstellen, einen für jede Bedingung.

Was die allgemeine Listentransposition betrifft, funktioniert dies:

def transpose[T](l: List[List[T]]): List[List[T]] = l match {
  case Nil => Nil
  case Nil :: _ => Nil
  case _ => (l map (_.head)) :: transpose(l map (_.tail))
}
Daniel C. Sobral
quelle
Dies funktioniert nicht für Listen beliebiger Größe. Zum Beispiel: transponieren (Liste (Liste ("a", "b"), Liste ("c")))
Venkat Sudheer Reddy Aedama
1
@VenkatSudheerReddyAedama Die Umsetzung unvollständiger Matrizen macht für mich keinen Sinn. Um Ihr Beispiel zu nehmen, ob cim Einklang mit aoder mit b? Und wie würden Sie es im Einklang mit dem anderen darstellen?
Daniel C. Sobral
Einverstanden. Das ist eine unvollständige Matrix. Ich habe nach etwas in der Art von zipAll gesucht. Sagen wir in meinem Fall, cstimmt es mit a(dh mit dem Index überein)?
Venkat Sudheer Reddy Aedama
2

Produktkollektionen haben einen flatZipBetrieb bis zur Arität 22.

scala> List(1,2,3) flatZip Seq("a","b","c") flatZip Vector(1.0,2.0,3.0) flatZip Seq(9,8,7)
res1: com.github.marklister.collections.immutable.CollSeq4[Int,String,Double,Int] = 
CollSeq((1,a,1.0,9),
        (2,b,2.0,8),
        (3,c,3.0,7))
Mark Lister
quelle
0

Mit Scalaz:

import scalaz.Zip
import scalaz.std.list._

// Zip 3
Zip[List].ap.tuple3(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"))

// Zip 4
Zip[List].ap.tuple4(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"))

// Zip 5
Zip[List].ap.tuple5(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"))

Für mehr als 5:

// Zip 6
Zip[List].ap.apply6(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"),
                    List("a6", "b6"))((_, _, _, _, _, _))

// Zip 7
Zip[List].ap.apply7(List("a1", "b1"),
                    List("a2", "b2"),
                    List("a3", "b3"),
                    List("a4", "b4"),
                    List("a5", "b5"),
                    List("a6", "b6"),
                    List("a7", "b7"))((_, _, _, _, _, _, _))

...

// Zip 12
Zip[List].ap.apply12(List("a1", "b1"),
                     List("a2", "b2"),
                     List("a3", "b3"),
                     List("a4", "b4"),
                     List("a5", "b5"),
                     List("a6", "b6"),
                     List("a7", "b7"),
                     List("a8", "b8"),
                     List("a9", "b9"),
                     List("a10", "b10"),
                     List("a11", "b11"),
                     List("a12", "b12"))((_, _, _, _, _, _, _, _, _, _, _, _))
ZhekaKozlov
quelle