¿Puedo comprimir más de dos listas juntas en Scala?

93

Dada la siguiente lista de Scala:

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

Como puedo conseguir:

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

Dado que zip solo se puede usar para combinar dos Listas, creo que necesitaría iterar / reducir la Lista principal de alguna manera. No es de extrañar que lo siguiente no funcione:

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)

¿Alguna sugerencia de cómo hacer esto? Creo que me falta una forma muy sencilla de hacerlo.

Actualización: Estoy buscando una solución que pueda tomar una Lista de N Listas con M elementos cada una y crear una Lista de M TupleN.

Actualización 2: resulta que es mejor para mi caso de uso específico tener una lista de listas, en lugar de una lista de tuplas, por lo que acepto la respuesta de calabaza. También es el más simple, ya que utiliza un método nativo.

pr1001
fuente
posible duplicado de secuencias múltiples Zip
Suma
Definitivamente vale la pena señalar: stackoverflow.com/questions/1683312/…
Venkat Sudheer Reddy Aedama
@VenkatSudheerReddyAedama También preguntado por mí, cinco días después. ;-)
pr1001

Respuestas:

36

No creo que sea posible generar una lista de tuplas de tamaño arbitrario, pero la función de transposición hace exactamente lo que necesita si no le importa obtener una lista de listas en su lugar.

copumpkin
fuente
¡Gracias, eso funciona perfectamente! A medida que avanzo en mi caso de uso específico, veo que una lista de listas sería mejor de todos modos, ya que necesito mapear y reducir las diversas sublistas.
pr1001
2
@JoshCason en el sentido más estricto de "más de dos", seguro. De hecho, tres es más que dos. Interpreté la pregunta en el sentido más amplio de "más de dos", es decir, arbitrariamente muchos. Y en ese caso, no es posible hacer lo que la pregunta quiere, a menos que busque HLists y similares.
copumpkin
el enlace en la respuesta está roto, el nuevo enlace es scala-lang.org/api/2.12.1/scala/…
Ramesh Maharjan
215
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))

Para futura referencia.

Xorlev
fuente
32
Esto es ideal para comprimir tres listas. Es una pena que esto no funcione para más de tres listas :(
theon
2
Tenga en cuenta que esto debe estar en una tupla primero: zippedno es una función de List.
Nathaniel Ford
6
zippedestá obsoleto en Scala 2.13. en 2.13, hacerl1.lazyZip(l2).lazyZip(l3).toList
Seth Tisue
30

Entonces, este fragmento de código no responderá a las necesidades del OP, y no solo porque se trata de un hilo de cuatro años, sino que responde a la pregunta del título, y tal vez a alguien incluso le resulte útil.

Para comprimir 3 colecciones:

as zip bs zip cs map { 
  case ((a,b), c) => (a,b,c)
}
Bijou Trouvaille
fuente
hacer 4 colecciones se parece 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, acorta aas zip bs zip cs zip ds map {case (((a,b),c),d)=>(a,b,c,d) }
sigue codificando
Agradable para listas de diferentes tipos.
FP Freely
11

Sí, con zip3 .

Harold L
fuente
2
Gracias, pero solo funciona con 3 listas. Estoy buscando una solución que pueda tomar una Lista de N Listas con M elementos cada una y crear una Lista de M TupleN.
pr1001
6

transposeHace el truco. Un posible algoritmo es:

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

Por ejemplo:

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 respuesta se trunca al tamaño de la lista más corta de la entrada.

combineLists(List(1, 2, 3), List(10,20))
// => List[List[Int]] = List(List(1, 10), List(2, 20))
WP McNeill
fuente
1
esta respuesta casi hace el truco, sin embargo, invierte los elementos. ¿Puede sugerir una versión mejorada que produzca la salida en el orden esperado? gracias
fracca
Versión modificada que conserva el orden: 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

Golosinas Scala todos sus diferentes tamaños de tupla como diferentes clases ( Tuple1, Tuple2, Tuple3, Tuple4, ...,Tuple22 ), mientras que lo hacen todos heredan de laProduct característica, rasgo que no lleva suficiente información para utilizar realmente los valores de datos de los diferentes tamaños de las tuplas si todos pudieran ser devueltos por la misma función. (Y los genéricos de Scala tampoco son lo suficientemente potentes para manejar este caso).

Su mejor opción es escribir sobrecargas de la función zip para los 22 tamaños de tuplas. Un generador de código probablemente te ayudaría con esto.

Ken Bloom
fuente
5

Si no desea seguir la ruta aplicativa scalaz / cats / (inserte su biblioteca funcional favorita aquí), la coincidencia de patrones es el camino a seguir, aunque la (_, _)sintaxis es un poco incómoda con el anidamiento, así que cambiémosla:

import scala.{Tuple2 => &}

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

El &es una elección arbitraria aquí, cualquier cosa que se ve bien infija debe hacerlo. Sin embargo, es probable que levante algunas cejas durante la revisión del código.

También debería funcionar con todo lo que pueda zip(por ejemplo, Futures)

L4Z
fuente
5

No creo que eso sea posible sin ser repetitivo. Por una simple razón: no puede definir el tipo de retorno de la función que está solicitando.

Por ejemplo, si su entrada fue List(List(1,2), List(3,4)), entonces el tipo de retorno sería List[Tuple2[Int]]. Si tuviera tres elementos, el tipo de retorno seríaList[Tuple3[Int]] , y así sucesivamente.

Puede regresar List[AnyRef], o incluso List[Product], y luego hacer un montón de casos, uno para cada condición.

En cuanto a la transposición general de la lista, esto funciona:

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
fuente
Esto no funcionará para listas de tamaño arbitrario. Por ejemplo: transpose (List (List ("a", "b"), List ("c")))
Venkat Sudheer Reddy Aedama
1
@VenkatSudheerReddyAedama La transposición de matrices incompletas no tiene sentido para mí. Para tomar su ejemplo, si está cen línea con ao con b? ¿Y cómo lo representaría en línea con el otro?
Daniel C. Sobral
Convenido. Esa es una matriz incompleta. Estaba buscando algo parecido a zipAll. Diga, en mi caso, ¿ cestá en línea con a(es decir, en línea con el índice)?
Venkat Sudheer Reddy Aedama
2

colecciones de productos tiene una flatZipoperación hasta aridad 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
fuente
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"))

Por más 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
fuente