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