斯卡拉。如何创建接受不同arities的元组的通用方法?

时间:2017-07-27 11:57:56

标签: scala scalaz shapeless

在我的应用程序中,我有很多地方,我需要获取一个元组列表,groupBy它由元组的第一个元素组成,并从其余元素中删除它。例如,我有元组

(1, "Joe", "Account"), (1, "Tom", "Employer"), (2, "John", "Account"), and result should be Map(1 -> List(("Joe", "Account"), ("Joe", "Account")), 2 -> List(("John", "Account")))

很容易实现

data.groupBy(_._1).map { case (k, v) => k -> v.map(f => (f._2, f._3)) }

但我正在寻找一般的解决方案,因为我可以拥有不同的元组,2,3,4或甚至7的元组。 我认为Shapeless或Scalaz可以帮助我,但我在这些库中的经验很少,请指出一些例子

1 个答案:

答案 0 :(得分:6)

这可以使用shapeless轻松实现(为简单起见,我不会将其概括为所有集合类型)。元组的特定类型类可以将它们解构为头部和尾部,称为IsComposite

import shapeless.ops.tuple.IsComposite

def groupTail[P, H, T](tuples: List[P])(
    implicit ic: IsComposite.Aux[P, H, T]): Map[H, List[T]] = {
  tuples
        .groupBy(ic.head)
        .map { case (k, vs) => (k, vs.map(ic.tail)) }
}

这适用于您的情况:

val data =
  List((1, "Joe", "Account"), (1, "Tom", "Employer"), (2, "John", "Account"))

assert {
  groupTail(data) == Map(
    1 -> List(("Joe", "Account"), ("Tom", "Employer")),
    2 -> List(("John", "Account"))
  )
}

以及不同类型的Tuple4

val data2 = List((1, 1, "a", 'a), (1, 2, "b", 'b), (2, 1, "a", 'b))

assert {
  groupTail(data2) == Map(
    1 -> List((1, "a", 'a), (2, "b", 'b)),
    2 -> List((1, "a", 'b))
  )
}

可运行代码at Scastie