最近我遇到了一个非常有用的 groupBy 函数,Groovy在Iterable上提供了这个函数:
public static Map groupBy(Iterable self, List<Closure> closures)
您可以使用它在列表甚至地图see example by mrhaki here上执行递归 groupBy
我想编写一个在Scala中执行相同操作的函数。但刚刚开始我的Scala之旅,我对于如何定义和实现此方法感到很遗憾。特别是函数的泛型方面以及此方法签名的返回类型超出了我的水平。
我需要更多有经验的Scala开发人员来帮助我。
以下签名是完全错误还是我在球场?
def groupBy[A, K[_]](src: List[A], fs: Seq[(A) ⇒ K[_]]): Map[K[_], List[A]]
另外,如何使用正确的类型实现递归?
答案 0 :(得分:5)
这是一个简单的多组实现:
uistack
如果你真的需要一些递归类型,你需要一个包装器:
implicit class GroupOps[A](coll: Seq[A]) {
def groupByKeys[B](fs: (A => B)*): Map[Seq[B], Seq[A]] =
coll.groupBy(elem => fs map (_(elem)))
}
val a = 1 to 20
a.groupByKeys(_ % 3, _ % 2) foreach println
将定义更改为:
sealed trait RecMap[K, V]
case class MapUnit[K, V](elem: V) extends RecMap[K, V] {
override def toString = elem.toString()
}
case class MapLayer[K, V](map: Map[K, RecMap[K, V]]) extends RecMap[K, V] {
override def toString = map.toString()
}
和implicit class GroupOps[A](coll: Seq[A]) {
def groupByKeys[B](fs: (A => B)*): Map[Seq[B], Seq[A]] =
coll.groupBy(elem => fs map (_(elem)))
def groupRecursive[B](fs: (A => B)*): RecMap[B, Seq[A]] = fs match {
case Seq() => MapUnit(coll)
case f +: fs => MapLayer(coll groupBy f mapValues {_.groupRecursive(fs: _*)})
}
}
会产生与问题更相关的内容
最后我从引用文章重建域名定义:
a.groupRecursive(_ % 3, _ % 2)
现在我们可以写
case class User(name: String, city: String, birthDate: Date) {
override def toString = name
}
implicit val date = new SimpleDateFormat("yyyy-MM-dd").parse(_: String)
val month = new SimpleDateFormat("MMM").format (_:Date)
val users = List(
User(name = "mrhaki", city = "Tilburg" , birthDate = "1973-9-7"),
User(name = "bob" , city = "New York" , birthDate = "1963-3-30"),
User(name = "britt" , city = "Amsterdam", birthDate = "1980-5-12"),
User(name = "kim" , city = "Amsterdam", birthDate = "1983-3-30"),
User(name = "liam" , city = "Tilburg" , birthDate = "2009-3-6")
)
并获取
地图(蒂尔堡 - &gt;地图(Mar - &gt; List(liam),Sep - &gt; List(mrhaki)),纽约 - &GT;地图(Mar - &gt; List(bob)),阿姆斯特丹 - &gt;地图(Mar - &gt; List(kim),May - &gt; List(britt)))
答案 1 :(得分:1)
由于方法完全不同,我决定添加另一个答案。
你可以,实际上得到非包装正确类型的地图与巨大的变通方法。我不是很擅长这个,所以有机会可以简化。
技巧 - 创建类型化函数序列,最近使用type classes
和type path
方法生成多级地图。
所以这是解决方案
sealed trait KeySeq[-V] {
type values
}
case class KeyNil[V]() extends KeySeq[V] {
type values = Seq[V]
}
case class KeyCons[K, V, Next <: KeySeq[V]](f: V => K, next: Next)
(implicit ev: RecGroup[V, Next]) extends KeySeq[V] {
type values = Map[K, Next#values]
def #:[K1](f: V => K1) = new KeyCons[K1, V, KeyCons[K, V, Next]](f, this)
}
trait RecGroup[V, KS <: KeySeq[V]] {
def group(seq: Seq[V], ks: KS): KS#values
}
implicit def groupNil[V]: RecGroup[V, KeyNil[V]] = new RecGroup[V, KeyNil[V]] {
def group(seq: Seq[V], ks: KeyNil[V]) = seq
}
implicit def groupCons[K, V, Next <: KeySeq[V]](implicit ev: RecGroup[V, Next]): RecGroup[V, KeyCons[K, V, Next]] =
new RecGroup[V, KeyCons[K, V, Next]] {
def group(seq: Seq[V], ks: KeyCons[K, V, Next]) = seq.groupBy(ks.f) mapValues (_ groupRecursive ks.next)
}
implicit def funcAsKey[K, V](f: V => K): KeyCons[K, V, KeyNil[V]] =
new KeyCons[K, V, KeyNil[V]](f, KeyNil[V]())
implicit class GroupOps[V](coll: Seq[V]) {
def groupRecursive[KS <: KeySeq[V]](ks: KS)(implicit g: RecGroup[V, KS]) =
g.group(coll, ks)
}
关键函数由#:
右关联运算符
所以如果我们定义
def mod(m:Int) = (x:Int) => x % m
def even(x:Int) = x % 2 == 0
然后
1 to 30 groupRecursive (even _ #: mod(3) #: mod(5) )
会产生正确的Map[Boolean,Map[Int,Map[Int,Int]]]
!!!
如果我们想从之前的问题
users.groupRecursive(((u:User)=> u.city(0)) #: ((u:User) => month(u.birthDate)))
我们正在构建Map[Char,Map[String,User]]
!