Puis-je compresser plus de deux listes ensemble dans Scala?

92

Compte tenu de la liste Scala suivante:

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

Comment puis-je avoir:

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

Puisque zip ne peut être utilisé que pour combiner deux listes, je pense que vous auriez besoin d'itérer / réduire la liste principale d'une manière ou d'une autre. Sans surprise, ce qui suit ne fonctionne pas:

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)

Des suggestions pour faire cela? Je pense qu'il me manque un moyen très simple de le faire.

Mise à jour: je recherche une solution qui peut prendre une liste de N listes avec M éléments chacune et créer une liste de M TupleNs.

Mise à jour 2: En fait, il est préférable pour mon cas d'utilisation spécifique d'avoir une liste de listes, plutôt qu'une liste de tuples, donc j'accepte la réponse de Pumpkin. C'est aussi le plus simple, car il utilise une méthode native.

pr1001
la source
duplication possible de plusieurs séquences Zip
Suma
À noter: stackoverflow.com/questions/1683312/…
Venkat Sudheer Reddy Aedama
@VenkatSudheerReddyAedama J'ai également demandé par moi, cinq jours plus tard. ;-)
pr1001

Réponses:

36

Je ne pense pas qu'il soit possible de générer une liste de tuples de taille arbitraire, mais la fonction de transposition fait exactement ce dont vous avez besoin si cela ne vous dérange pas d'obtenir une liste de listes à la place.

copumpkin
la source
Merci, cela fonctionne parfaitement! Au fur et à mesure que j'entre dans mon cas d'utilisation spécifique, je vois qu'une liste de listes serait meilleure de toute façon, car j'ai besoin de mapper et de réduire les différentes sous-listes.
pr1001
2
@JoshCason dans le sens le plus étroit de «plus de deux», bien sûr. Trois c'est en effet plus de deux. J'ai interprété la question dans le sens plus large de «plus de deux», signifiant arbitrairement plusieurs. Et dans ce cas, il n'est pas possible de faire ce que veut la question, à moins que vous n'atteigniez HLists et autres.
copumpkin
le lien dans la réponse est cassé, le nouveau lien est 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))

Pour référence future.

Xorlev
la source
32
C'est idéal pour zipper trois listes. Dommage que cela ne fonctionne pas pour plus de trois listes :(
theon
2
Notez que cela doit d'abord être dans un tuple: zippedn'est pas une fonction de List.
Nathaniel Ford
6
zippedest obsolète dans Scala 2.13. en 2.13, fairel1.lazyZip(l2).lazyZip(l3).toList
Seth Tisue
30

Donc, ce morceau de code ne répondra pas aux besoins de l'OP, et pas seulement parce qu'il s'agit d'un fil de discussion vieux de quatre ans, mais il répond à la question du titre, et peut-être que quelqu'un pourrait même le trouver utile.

Pour zipper 3 collections:

as zip bs zip cs map { 
  case ((a,b), c) => (a,b,c)
}
Bijou Trouvaille
la source
faire 4 collections ressemble à: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, vous raccourcissez àas zip bs zip cs zip ds map {case (((a,b),c),d)=>(a,b,c,d) }
Keepscoding
Bien pour les listes de types variés.
FP Librement
11

Oui, avec zip3 .

Harold L
la source
2
Merci, mais cela ne fonctionne qu'avec 3 listes. Je recherche une solution qui peut prendre une liste de N listes avec M éléments chacune et créer une liste de M TupleNs.
pr1001
6

transposefait l'affaire. Un algorithme possible est:

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

Par exemple:

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

La réponse est tronquée à la taille de la liste la plus courte dans l'entrée.

combineLists(List(1, 2, 3), List(10,20))
// => List[List[Int]] = List(List(1, 10), List(2, 20))
WP McNeill
la source
1
cette réponse fait presque l'affaire, cependant, elle inverse les éléments. Pouvez-vous suggérer une version améliorée qui produit la sortie dans l'ordre prévu? merci
fracca
Version modifiée qui conserve la commande: 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 traite toutes ses différentes tailles de tuple que différentes classes ( Tuple1, Tuple2, Tuple3, Tuple4, ..., Tuple22) alors qu'ils font tout Hériter du Producttrait, ce trait ne porte pas suffisamment d' informations pour utiliser réellement les valeurs de données des différentes tailles de tuples s'ils pouvaient tous être retournés par la même fonction. (Et les génériques de scala ne sont pas non plus assez puissants pour gérer ce cas.)

Votre meilleur pari est d'écrire des surcharges de la fonction zip pour les 22 tailles de Tuple. Un générateur de code vous aiderait probablement.

Ken Bloom
la source
5

Si vous ne voulez pas suivre la route applicative scalaz / cats / (insérez votre bibliothèque fonctionnelle préférée ici), la correspondance de motifs est la voie à suivre, bien que la (_, _)syntaxe soit un peu gênante avec l'imbrication, changeons-la:

import scala.{Tuple2 => &}

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

le & un choix arbitraire ici, tout ce qui a l'air sympa devrait le faire. Cependant, vous aurez probablement quelques sourcils levés lors de la révision du code.

Cela devrait également fonctionner avec tout ce que vous pouvez zip(par exemple, Futures)

L4Z
la source
5

Je ne crois pas que ce soit possible sans être répétitif. Pour une raison simple: vous ne pouvez pas définir le type de retour de la fonction que vous demandez.

Par exemple, si votre entrée était List(List(1,2), List(3,4)), alors le type de retour serait List[Tuple2[Int]]. S'il avait trois éléments, le type de retour serait List[Tuple3[Int]], et ainsi de suite.

Vous pourriez revenir List[AnyRef], ou même List[Product], puis faire un tas de cas, un pour chaque condition.

En ce qui concerne la transposition générale de la liste, cela fonctionne:

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
la source
Cela ne fonctionnera pas pour les listes de taille arbitraire. Par exemple: transpose (List (List ("a", "b"), List ("c")))
Venkat Sudheer Reddy Aedama
1
@VenkatSudheerReddyAedama La transposition de matrices incomplètes n'a pas de sens pour moi. Pour prendre votre exemple, si cen ligne avec aou avec b? Et comment le représenteriez-vous en ligne avec l'autre?
Daniel C. Sobral
D'accord. C'est une matrice incomplète. Je cherchais quelque chose du genre zipAll. Dites dans mon cas, cest en ligne avec a(c'est-à-dire en ligne avec index)?
Venkat Sudheer Reddy Aedama
2

product-collections a une flatZipopération jusqu'à l'arité 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
la source
0

Avec 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"))

Pour plus de 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
la source