压缩多个序列

时间:2012-03-09 09:59:02

标签: scala tuples

我正在尝试zip多个序列形成一个长元组:

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

我得到了什么:

List[(((Int, Char), String), Boolean)] =
  List((((1,a),Alpha),true), (((2,b),Beta),false), (((3,c),Gamma),false))

但是我想获得一系列 flat 元组:

List[(Int, Char, String, Boolean)] = 
  List((1,a,Alpha,true), (2,b,Beta,false), (3,c,Gamma,false))

我现在能做到:

List(ints, chars, strings, bools).transpose

但它会返回弱类型的List[List[Any]]。我也可以(ints, chars, strings).zipped,但zipped仅适用于2元组和3元组。

有没有办法轻松压缩(任意)数量的等长序列?

7 个答案:

答案 0 :(得分:11)

这是解决您的示例的一种方法,但这不适用于任意数量的序列。

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

我认为对于任意长度的元组一般都不可能这样做,至少不是这种解决方案。元组是强类型的,并且类型系统不允许您指定可变数量的类型参数,据我所知,这使得无法制作f2和{{1}的通用版本采用任意长度f3的元组(将返回元组((A,B),C,D,...))。

如果有办法指定可变数量的类型参数,我们在Scala的标准库中不需要特征(A,B,C,D,...)Tuple1,... Tuple2

答案 1 :(得分:6)

我会创建一个代表数据集的类:

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

这为访问值提供了更好的名称,而不是我们在元组中的_N。如果列表可以具有不同的大小,则应选择最短的列表:

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

现在可以提取数据:

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

当原始列表可以包含大量值时,最好将它们设置为IndexedSeq,以便访问时间为O(1)。

答案 2 :(得分:5)

使用shapeless,您可以:

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

(不确定,如果定义的隐含更多,可以避免map和来回转换)

[编辑:此部分不再适用。

请注意,无形文档说,目前仅支持最多Tuple4的转化。您必须手动创建HLists。]

答案 3 :(得分:5)

我认为模式匹配是一个不错的选择

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

或者您也可以添加类型

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

答案 4 :(得分:2)

我同意Jesper的观点,即一般情况下这是不可能的,因为每个元组都在源代码中表示为单独的类,因此除非使用代码生成器,否则必须编写单独的代码才能访问它们。

但我想添加另一种可能的解决方案。如果你想保留你的元组条目的类型,但是对于更像集合的类型感兴趣,也许HLists(异类列表)适合你。您可以google hlist scala进行实施和解释。

答案 5 :(得分:1)

使用product-collections

scala> ints flatZip chars flatZip strings flatZip bools
res0: org.catch22.collections.immutable.CollSeq4[Int,Char,String,Boolean] = 
CollSeq((1,a,Alpha,true),
        (2,b,Beta,false),
        (3,c,Gamma,false))

目前适用于arity 1 - 22.您可以看到类型被保留。

答案 6 :(得分:0)

这是另一个可以解决您问题的解决方案。

ints zip chars zip strings zip bools map{ case (((a, b), c), d) => (a,b,c,d)}