我希望groupByIndex
函数根据索引(而不是值)对值进行分组。
Vector[A]
的具体方法定义如下所示:
def groupByIndex[A, K](vector: Vector[A], f: Int => K): immutable.Map[K, Vector[(A, Int)]] = {
vector.zipWithIndex.groupBy { case (elem, index) => f(index) }
}
在REPL中测试此函数确实给出了正确的结果:
scala> val vec = Vector.tabulate(4)(i => s"string ${i+1}")
vec: scala.collection.immutable.Vector[String] = Vector(string 1, string 2, string 3, string 4)
scala> groupByIndex(vec, i => i%2)
res2: scala.collection.immutable.Map[Int,Vector[(String, Int)]] = Map(1 -> Vector((string 2,1), (string 4,3)), 0 -> Vector((string 1,0), (string 3,2)))
现在,我想应用“enrich-my-library”模式将此方法提供给应该支持它的所有类,即实现zipWithIndex
和groupBy
的类。这两种方法在GenIterableLike
(zipWithIndex
)和GenTraversableLike
/ TraversableLike
(groupBy
)中定义。
考虑到这一切,我试图模仿zipWithIndex
的方法定义(这是有问题的)和groupBy
来构建我自己的groupByIndex
:
implicit class GenIterableLikeOps[A, Repr](val iterable: GenIterableLike[A, Repr] with TraversableLike[A, Repr]) extends AnyVal {
def groupByIndex[K, A1 >: A, That <: TraversableLike[(A1, Int), OtherRepr], OtherRepr](f: Int => K)(implicit bf: CanBuildFrom[Repr, (A1, Int), That]): immutable.Map[K, OtherRepr] = {
val zipped = iterable.zipWithIndex
zipped.groupBy{ case (elem, index) => f(index) }
}
}
首先,这对我来说似乎太复杂了 - 有没有办法简化这个?例如,我们可以以某种方式删除第二个OtherRepr
吗? (我无法。)
其次,如果没有明确指定泛型参数,我无法调用此函数。使用上面的示例我收到以下错误:
scala> vec.groupByIndex(i => i%2)
<console>:21: error: Cannot construct a collection of type scala.collection.TraversableLike[(String, Int),Nothing] with elements of type (String, Int) based on a collection of type scala.collection.immutable.Vector[String].
vec.groupByIndex(i => i%2)
^
scala> vec.groupByIndex[Int, String, Vector[(String, Int)], Vector[(String, Int)]](i => i%2)
res4: scala.collection.immutable.Map[Int,Vector[(String, Int)]] = Map(1 -> Vector((string 2,1), (string 4,3)), 0 -> Vector((string 1,0), (string 3,2)))
我如何a)简化这种方法,b)使其工作而不必指定通用参数?
答案 0 :(得分:2)
您可以OtherThat
替换That
类型参数。这样你就摆脱了OtherThat
并解决了必须指定泛型类型参数的问题。然后,编译器可以通过查看That
的隐式值来解析CanBuildFrom[Repr, (A1, Int), That]
。
implicit class GenIterableLikeOps[A, Repr]
(val iterable: GenIterableLike[A, Repr] with TraversableLike[A, Repr])
extends AnyVal {
def groupByIndex
[K, A1 >: A, That <: TraversableLike[(A1, Int), That]]
(f: Int => K)(implicit bf: CanBuildFrom[Repr, (A1, Int), That])
: Map[K, That] = {
val zipped = iterable.zipWithIndex
zipped.groupBy{ case (elem, index) => f(index) }
}
}
答案 1 :(得分:1)
这不如其他答案好,但是如果你不关心你正在构建什么,那么简化和避免构建压缩集合的一种方法是:
implicit class gbi[A](val as: Traversable[A]) extends AnyVal {
def groupByIndex[K](f: Int => K) = (as, (0 until Int.MaxValue)).zipped.groupBy { case (x, i) => f(i) }
}
该范围是一种避免使用可穿越大小的良性方法。