scala functional-programming list zip

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



functional-programming zip (10)

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"))((_, _, _, _, _, _, _, _, _, _, _, _))

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

Como zip solo se puede usar para combinar dos listas, creo que tendrías que iterar / reducir la lista principal de alguna manera. No es sorprendente 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 uno cómo hacer esto? Creo que me falta una forma muy simple 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 TupleNs.

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, así que estoy aceptando la respuesta de calabaza. También es el más simple, ya que utiliza un método nativo.


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

Para comprimir 3 colecciones:

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


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

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

Puede devolver List [AnyRef], o incluso List [Product], y luego crear varios 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)) }


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.



Scala trata a todos sus diferentes tamaños de tupla como clases diferentes ( Tuple1 , Tuple2 , Tuple3 , Tuple4 , ..., Tuple22 ) mientras todos heredan del rasgo del Product , ese rasgo no lleva suficiente información para usar realmente los valores de los datos de los diferentes tamaños de 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 Tuple. Un generador de código probablemente te ayude con esto.


Si no quieres ir por la ruta aplicable scalaz / cats / (inserta tu lib funcional favorita aquí), la coincidencia de patrones es el camino a seguir, aunque la sintaxis (_, _) es un poco incómoda con la anidación, así que vamos a cambialo:

import scala.{Tuple2 => &} for (i1 & i2 & i3 & i4 <- list1 zip list2 zip list3 zip list4) yield (i1, i2, i3, i4)

El & es una opción arbitraria aquí, cualquier cosa que se ve bien infijo debería hacerlo. Sin embargo, es probable que obtengas algunas cejas levantadas durante la revisión del código.

También debería funcionar con cualquier cosa que pueda zip (por ejemplo, Future s)


product-collections tiene una operación flatZip hasta flatZip 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))


transpose hace el truco. Un posible algoritmo es:

def combineLists[A](ss:List[A]*) = (ss.head.map(List(_)) /: ss.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(100, 10, 1), List(200, 20, 2))

La respuesta se trunca al tamaño de la lista más corta en la entrada.

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


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.