Zip multiple sequences

Here's one way to solve your example, but this is not for an arbitrary number of sequences.

val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)

val input = ints zip chars zip strings zip bools

// Flattens a tuple ((A,B),C) into (A,B,C)
def f2[A,B,C](t: ((A,B),C)) = (t._1._1, t._1._2, t._2)

// Flattens a tuple ((A,B),C,D) into (A,B,C,D)
def f3[A,B,C,D](t: ((A,B),C,D)) = (t._1._1, t._1._2, t._2, t._3)

input map f2 map f3

I don't think it is possible to do it generically for tuples of arbitrary length, at least not with this kind of solution. Tuples are strongly-typed, and the type system doesn't allow you to specify a variable number of type parameters, as far as I know, which makes it impossible to make a generalized version of f2 and f3 that takes a tuple of arbitrary length ((A,B),C,D,...) (that would return a tuple (A,B,C,D,...)).

If there were a way to specify a variable number of type parameters, we wouldn't need traits Tuple1, Tuple2, ... Tuple22 in Scala's standard library.


Using shapeless, you could do:

import shapeless.Tuples._

val ints = (1, 2, 3)
val chars = ('a', 'b', 'c')

val megatuple = (ints, chars)

val megahlist = (megatuple hlisted) map hlisted

val transposed = (mhlist transpose) map tupled tupled

scala> transposed
res: ((Int, Char), (Int, Char), (Int, Char)) = ((1,a),(2,b),(3,c))

(not sure, if there are more implicts defined which lets you avoid the map and back-and-forth conversions)

[Edit: This part is not true anymore.

Note that the shapeless docs say, only conversions up to Tuple4 are currently supported. You’d have to manually create the HLists then.]


I would create a class which represents the data sets:

case class DataSet(int: Int, char: Char, string: String, bool: Boolean)

This brings nicer names for accessing the values instead of _N we have in tuples. If the lists can have different sizes the shortest should be chosen:

val min = List(ints, chars, strings, bools).map(_.size).min

Now it is possible to extract the data:

val dataSets = (0 until min) map { i => DataSet(ints(i), chars(i), strings(i), bools(i)) }

When the original lists can contain a lot of values it is better to make them to a IndexedSeq so that the access time is O(1).


I think pattern matching is a good option

val ints = List(1,2,3)
val chars = List('a', 'b', 'c')
val strings = List("Alpha", "Beta", "Gamma")
val bools = List(true, false, false)
(ints zip chars zip strings zip bools) map { case (((i,c),s),b) => (i,c,s,b)}

**res1: List[(Int, Char, java.lang.String, Boolean)] = List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))**

or you can add type as well

(ints zip chars zip strings zip bools) map {case (((i:Int,c:Char),s:String),b:Boolean) => (i,c,s,b)}

**res2: List[(Int, Char, java.lang.String, Boolean)] = List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))**

Tags:

Tuples

Scala