Posso comprimere più di due elenchi insieme in Scala?


92

Dato il seguente elenco Scala:

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

Come posso avere:

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

Poiché zip può essere utilizzato solo per combinare due elenchi, penso che dovresti iterare / ridurre in qualche modo l'elenco principale. Non sorprende che quanto segue non funzioni:

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)

Qualche suggerimento su come farlo? Penso che mi manchi un modo molto semplice per farlo.

Aggiornamento: sto cercando una soluzione che possa prendere un elenco di N elenchi con M elementi ciascuno e creare un elenco di M TupleN.

Aggiornamento 2: a quanto pare è meglio per il mio caso d'uso specifico avere un elenco di elenchi, piuttosto che un elenco di tuple, quindi accetto la risposta di Pumpkin. È anche il più semplice, poiché utilizza un metodo nativo.


possibile duplicato di sequenze multiple Zip
Suma


@VenkatSudheerReddyAedama Chiesto anche da me, cinque giorni dopo. ;-)
pr1001

Risposte:


36

Non credo sia possibile generare un elenco di tuple di dimensioni arbitrarie, ma la funzione di trasposizione fa esattamente ciò di cui hai bisogno se non ti dispiace invece ottenere un elenco di elenchi.


Grazie, funziona perfettamente! Entrando nel mio caso d'uso specifico, vedo che un elenco di elenchi sarebbe comunque migliore, poiché ho bisogno di mappare e ridurre i vari elenchi secondari.
pr1001

2
@JoshCason nel senso più stretto di "più di due", certo. Tre è davvero più di due. Ho interpretato la domanda nel senso più ampio di "più di due", intendendo arbitrariamente molti. E in quel caso, non è possibile fare ciò che la domanda vuole, a meno che non si raggiunga se HListe simili.
copumpkin

il collegamento nella risposta è interrotto, il nuovo collegamento è 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))

Per riferimento futuro.


32
Questo è ottimo per comprimere tre elenchi. Peccato che questo non funzioni per più di tre elenchi :(
theon

2
Si noti che questo deve essere prima in una tupla: zippednon è una funzione di List.
Nathaniel Ford

6
zippedè deprecato in Scala 2.13. in 2.13, dol1.lazyZip(l2).lazyZip(l3).toList
Seth Tisue

30

Quindi questo pezzo di codice non risponderà alle esigenze dell'OP, e non solo perché si tratta di un thread vecchio di quattro anni, ma risponde alla domanda del titolo, e forse qualcuno potrebbe persino trovarlo utile.

Per comprimere 3 raccolte:

as zip bs zip cs map { 
  case ((a,b), c) => (a,b,c)
}

per fare 4 raccolte assomiglia a: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, si accorcia aas zip bs zip cs zip ds map {case (((a,b),c),d)=>(a,b,c,d) }
keepscoding

Bello per elenchi di vario tipo.
FP liberamente

11

Sì, con zip3 .


2
Grazie, ma funziona solo con 3 elenchi. Sto cercando una soluzione che possa prendere un elenco di N elenchi con M elementi ciascuno e creare un elenco di M TupleN.
pr1001

6

transposefa il trucco. Un possibile algoritmo è:

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

Per esempio:

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 risposta viene troncata alla dimensione dell'elenco più breve nell'input.

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

1
questa risposta quasi fa il trucco, tuttavia, inverte gli elementi. Potete suggerire una versione migliorata che produca l'output nell'ordine previsto? grazie
fracca

Versione modificata che mantiene l'ordine: 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 tratta tutte le sue diverse dimensioni tupla come classi diverse ( Tuple1, Tuple2, Tuple3, Tuple4, ..., Tuple22), mentre fanno tutto ereditare dalla Productcaratteristica, quella caratteristica non trasportano informazioni sufficienti per utilizzare effettivamente i valori dei dati dalle diverse dimensioni di tuple se potessero essere restituiti tutti dalla stessa funzione. (E nemmeno i generici di scala non sono abbastanza potenti per gestire questo caso.)

La soluzione migliore è scrivere sovraccarichi della funzione zip per tutte le 22 dimensioni di tuple. Un generatore di codice probabilmente ti aiuterebbe con questo.


5

Se non vuoi seguire il percorso applicativo scalaz / cats / (inserisci la tua libreria funzionale preferita qui), il pattern matching è la strada da percorrere, anche se la (_, _)sintassi è un po 'scomoda con l'annidamento, quindi cambiamola:

import scala.{Tuple2 => &}

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

Il &è una scelta arbitraria qui, tutto ciò che sembra piacevole infisso dovrebbe farlo. Tuttavia, probabilmente avrai qualche sopracciglio inarcato durante la revisione del codice.

Dovrebbe funzionare anche con tutto ciò che puoi zip(ad es Future.


5

Non credo che sia possibile senza essere ripetitivo. Per un semplice motivo: non puoi definire il tipo di restituzione della funzione che stai chiedendo.

Ad esempio, se il tuo input fosse List(List(1,2), List(3,4)), il tipo restituito sarebbe List[Tuple2[Int]]. Se avesse tre elementi, il tipo restituito sarebbe List[Tuple3[Int]]e così via.

Potresti tornare List[AnyRef], o anche List[Product], e poi fare un mucchio di casi, uno per ogni condizione.

Per quanto riguarda la trasposizione generale della Lista, funziona:

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

Questo non funzionerà per elenchi di dimensioni arbitrarie. Ad esempio: transpose (List (List ("a", "b"), List ("c")))
Venkat Sudheer Reddy Aedama

1
@VenkatSudheerReddyAedama La trasposizione di matrici incomplete non ha senso per me. Per fare il tuo esempio, se cin linea con ao con b? E come lo rappresenteresti essendo in linea con l'altro?
Daniel C. Sobral

Concordato. Questa è una matrice incompleta. Stavo cercando qualcosa sulla falsariga di zipAll. Di 'nel mio caso, cè in linea con a(cioè in linea con l'indice)?
Venkat Sudheer Reddy Aedama

2

product-collections ha flatZipun'operazione fino a arity 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))

0

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

Per più di 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"))((_, _, _, _, _, _, _, _, _, _, _, _))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.