Posso compactar mais de duas listas no Scala?

92

Dada a seguinte Lista de Scala:

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

Como posso obter:

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

Como zip só pode ser usado para combinar duas Listas, acho que você precisaria iterar / reduzir a Lista principal de alguma forma. Não é de surpreender que o seguinte não 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)

Alguma sugestão de como fazer isso? Acho que estou perdendo uma maneira muito simples de fazer isso.

Atualização: Estou procurando uma solução que pode pegar uma Lista de N Listas com M elementos cada e criar uma Lista de M TupleNs.

Atualização 2: Acontece que é melhor para meu caso de uso específico ter uma lista de listas, em vez de uma lista de tuplas, então estou aceitando a resposta do pumpkin. Também é o mais simples, pois usa um método nativo.

pr1001
fonte
possível duplicata de sequências múltiplas Zip
Suma
Definitivamente, vale a pena observar: stackoverflow.com/questions/1683312/…
Venkat Sudheer Reddy Aedama
@VenkatSudheerReddyAedama Também perguntei por mim, cinco dias depois. ;-)
pr1001

Respostas:

36

Não acredito que seja possível gerar uma lista de tuplas de tamanho arbitrário, mas a função transpor faz exatamente o que você precisa se você não se importar em obter uma lista de listas.

copumpkin
fonte
Obrigado, isso funciona perfeitamente! À medida que vou para meu caso de uso específico, vejo que uma lista de listas seria melhor de qualquer maneira, pois preciso mapear e reduzir as várias sublistas.
pr1001
2
@JoshCason no sentido estrito de "mais de dois", com certeza. Três é realmente mais do que dois. Interpretei a pergunta no sentido mais amplo de "mais de dois", significando arbitrariamente muitos. E, nesse caso, não é possível fazer o que a pergunta deseja, a menos que você alcance HLists e semelhantes.
Copumpkin
o link da resposta está quebrado, o novo link é 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))

Para referência futura.

Xorlev
fonte
32
Isso é ótimo para compactar três listas. É uma pena que isso não funcione para mais de três listas :(
theon
2
Observe que isso precisa estar em uma tupla primeiro: zippednão é uma função de List.
Nathaniel Ford
6
zippedestá obsoleto no Scala 2.13. em 2.13, façal1.lazyZip(l2).lazyZip(l3).toList
Seth Tisue
30

Portanto, este trecho de código não atenderá às necessidades do OP, e não apenas porque este é um segmento de quatro anos, mas responde à pergunta do título e talvez alguém possa até achar útil.

Para compactar 3 coleções:

as zip bs zip cs map { 
  case ((a,b), c) => (a,b,c)
}
Bijou Trouvaille
fonte
para fazer 4 coleções parece: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, você encurtou paraas zip bs zip cs zip ds map {case (((a,b),c),d)=>(a,b,c,d) }
codificando
Bom para listas de tipos variados.
FP Livremente
11

Sim, com zip3 .

Harold L
fonte
2
Obrigado, mas só funciona com 3 listas. Estou procurando uma solução que pode pegar uma lista de N listas com M elementos cada e criar uma lista de M TupleNs.
pr1001
6

transposefaz o truque. Um algoritmo possível é:

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

Por exemplo:

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

A resposta é truncada para o tamanho da lista mais curta na entrada.

combineLists(List(1, 2, 3), List(10,20))
// => List[List[Int]] = List(List(1, 10), List(2, 20))
WP McNeill
fonte
1
essa resposta quase resolve, no entanto, inverte os elementos. Você pode sugerir uma versão aprimorada que produza a saída na ordem esperada? obrigado
fracca
Versão modificada que mantém a ordem: 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

Trata Scala todos os seus diferentes tamanhos tupla como diferentes classes ( Tuple1, Tuple2, Tuple3, Tuple4, ..., Tuple22) enquanto eles fazem tudo herdar do Producttraço, essa característica não carrega informação suficiente para realmente usar os valores de dados entre os diferentes tamanhos de tuples se eles pudessem ser todos retornados pela mesma função. (E os genéricos do scala também não são poderosos o suficiente para lidar com este caso.)

Sua melhor aposta é escrever sobrecargas da função zip para todos os 22 tamanhos de tupla. Um gerador de código provavelmente o ajudaria com isso.

Ken Bloom
fonte
5

Se você não quiser seguir a rota do aplicativo scalaz / cats / (insira sua biblioteca funcional favorita aqui), a correspondência de padrões é o caminho a percorrer, embora a (_, _)sintaxe seja um pouco estranha com aninhamento, então vamos mudá-la:

import scala.{Tuple2 => &}

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

O &é uma escolha arbitrária aqui, qualquer coisa que pareça um bom infixo deve servir. Provavelmente, você ficará surpreso durante a revisão do código.

Também deve funcionar com qualquer coisa que você puder zip(por exemplo, Futures)

L4Z
fonte
5

Não acredito que seja possível sem ser repetitivo. Por um motivo simples: você não pode definir o tipo de retorno da função que está solicitando.

Por exemplo, se sua entrada foi List(List(1,2), List(3,4)), o tipo de retorno seria List[Tuple2[Int]]. Se tivesse três elementos, o tipo de retorno seria List[Tuple3[Int]], e assim por diante.

Você poderia retornar List[AnyRef], ou até mesmo List[Product], e então fazer um monte de casos, um para cada condição.

Quanto à transposição geral da Lista, 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
fonte
Isso não funciona para listas de tamanhos arbitrários. Por exemplo: transpose (List (List ("a", "b"), List ("c")))
Venkat Sudheer Reddy Aedama
1
@VenkatSudheerReddyAedama A transposição de matrizes incompletas não faz sentido para mim. Para tomar o seu exemplo, se estiver calinhado com aou com b? E como você representaria estar alinhado com o outro?
Daniel C. Sobral
Acordado. Essa é uma matriz incompleta. Eu estava procurando por algo nos moldes do zipAll. Digamos, no meu caso, cestá alinhado com a(ou seja, alinhado com o índice)?
Venkat Sudheer Reddy Aedama
2

a coleção de produtos tem uma flatZipoperação até aridade 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
fonte
0

Com 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 mais 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
fonte