Can I zip more than two lists together in Scala?


Can I zip more than two lists together in Scala?



Given the following Scala List:


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



How can I get:


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



Since zip can only be used to combine two Lists, I think you would need to iterate/reduce the main List somehow. Not surprisingly, the following doesn't work:


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)



Any suggestions one how to do this? I think I'm missing a very simple way to do it.



Update: I'm looking for a solution that can take a List of N Lists with M elements each and create a List of M TupleNs.



Update 2: As it turns out it is better for my specific use-case to have a list of lists, rather than a list of tuples, so I am accepting pumpkin's response. It is also the simplest, as it uses a native method.





possible duplicate of Zip multiple sequences
– Suma
Feb 26 '15 at 19:23





Definitely worth noting: stackoverflow.com/questions/1683312/…
– Venkat Sudheer Reddy Aedama
Jul 20 '15 at 23:28





@VenkatSudheerReddyAedama Also asked by me, five days later. ;-)
– pr1001
Jul 22 '15 at 15:47




10 Answers
10



I don't believe it's possible to generate a list of tuples of arbitrary size, but the transpose function does exactly what you need if you don't mind getting a list of lists instead.





Thanks, that works perfectly! As I go into my specific use case, I see that a list of lists would be better anyway, as I need to map and reduce the various sub-lists.
– pr1001
Nov 3 '09 at 1:38





@JoshCason in the narrowest sense of "more than two", sure. Three is indeed more than two. I interpreted the question in the broader sense of "more than two", meaning arbitrarily many. And in that case, it is not possible to do what the question wants, unless you reach for HLists and the like.
– copumpkin
May 3 '16 at 12:54


HList





the link in the answer is broken, new link is scala-lang.org/api/2.12.1/scala/…
– Ramesh Maharjan
Jun 22 at 8:52


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



For future reference.





This is great for zipping three lists. Shame this doesn't work for more than three list :(
– theon
Apr 22 '14 at 16:01





Do note that this needs to be in a tuple first: zipped is not a function of List.
– Nathaniel Ford
Dec 10 '15 at 0:11


zipped


List



So this piece of code won't answer the needs of the OP, and not only because this is a four year old thread, but it does answer the title question, and perhaps someone may even find it useful.



To zip 3 collections:


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





to do 4 collections looks like: as zip bs zip cs zip ds map { case ((a,b),c)} map {case ((a,b),c,d)=>(a,b,c,d)}
– James Tobin
Aug 25 '16 at 17:59


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





@JamesTobin, u shorten to as zip bs zip cs zip ds map {case (((a,b),c),d)=>(a,b,c,d) }
– keepscoding
May 12 '17 at 2:31



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





Nice for lists of varying type.
– F. P. Freely
Aug 31 '17 at 3:07



Yes, with zip3.





Thanks, but it only works with 3 lists. I'm looking for a solution that can take a List of N Lists with M elements each and create a List of M TupleNs.
– pr1001
Nov 3 '09 at 0:13



Scala treats all of its different tuple sizes as different classes (Tuple1, Tuple2, Tuple3, Tuple4,...,Tuple22) while they do all inherit from the Product trait, that trait doesn't carry enough information to actually use the data values from the different sizes of tuples if they could all be returned by the same function. (And scala's generics aren't powerful enough to handle this case either.)


Tuple1


Tuple2


Tuple3


Tuple4


Tuple22


Product



Your best bet is to write overloads of the zip function for all 22 Tuple sizes. A code generator would probably help you with this.



I don't believe that's possible without being repetitive. For one simple reason: you can't define the returning type of the function you are asking for.



For instance, if your input was List(List(1,2), List(3,4)), then the return type would be List[Tuple2[Int]]. If it had three elements, the return type would be List[Tuple3[Int]], and so on.



You could return List[AnyRef], or even List[Product], and then make a bunch of cases, one for each condition.



As for general List transposition, this works:


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





This won't work for arbitrary sized lists. For example: transpose(List(List("a", "b"), List("c")))
– Venkat Sudheer Reddy Aedama
Mar 12 '15 at 17:56






@VenkatSudheerReddyAedama Transposition of incomplete matrices doesn't make sense to me. To take your example, if c in line with a or with b? And how would you represent it being in line with the other?
– Daniel C. Sobral
Mar 12 '15 at 20:56


c


a


b





Agreed. That's an incomplete matrix. I was looking for something along the lines of zipAll. Say in my case, c is in line with a (i.e., in-line with index) ?
– Venkat Sudheer Reddy Aedama
Mar 12 '15 at 20:59


c


a



transpose does the trick. A possible algorithm is:


transpose


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



For example:


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



The answer is truncated to the size of the shortest list in the input.


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





this answer almost does the trick, however, it reverse the elements. Can you suggest an improved version that produces the output in the expected order? thanks
– fracca
Jun 12 '13 at 15:51





Modified version that retains the order: def combineLists[A](ss:List[A]*) = { val sa = ss.reverse; (sa.head.map(List(_)) /: sa.tail)(_.zip(_).map(p=>p._2 :: p._1)) }
– rogermenezes
Jun 29 at 22:20



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



If you don't want to go down the applicative scalaz/cats/(insert your favourite functional lib here) route, pattern matching is the way to go, although the (_, _) syntax is a bit awkward with nesting, so let's change it:


(_, _)


import scala.{Tuple2 => &}

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



The & is an arbitrary choice here, anything that looks nice infix should do it. You'll likely get a few raised eyebrows during code review, though.


&



It should also work with anything you can zip (e.g. Futures)


zip


Future



product-collections has aflatZip operation up to arity 22.


flatZip


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



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



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






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

How to make file upload 'Required' in Contact Form 7?

Rothschild family

amazon EC2 - How to make wp-config.php to writable?