我正在尝试为一个序列实现一个distinctOn函数,该函数将接受一个函数f并返回一个序列,当f应用于f时,每个项目都有一个不同的结果。 EG:
case class Person(name:String, age:Int)
val people = Seq(Person("Al", 20), Person("Bob", 21),
Person("Bob", 24)).distinctOn(_.name)
//people should be:
Seq(Person("Al", 20), Person("Bob", 21))
返回第一个副本(A1),并保留顺序。我当前的实现包含一个var,而我使用Sets和GroupBy的其他尝试都没有保留顺序。有没有var的更好的方法来实现它?为了记录,我目前的尝试是:
def distinctOn[A](f: T => A):Seq[T]={
var seen = Set[A]()
seq.foldLeft(Seq[T]()) { (res, curr) => {
if(!seen.contains(f(curr))){
seen = seen ++ Set[A](f(curr))
res ++ Seq(curr)
}else{
res
}
}}
}
答案 0 :(得分:6)
这是一个实现,它在适用的情况下保留顺序,也适用于除Traversable
以外的其他Seq
个。它基于distinct
的实现,并使用其他集合方法中使用的构建器工厂(a.k.a。CanBuildFrom
)。
class TraversableOnceExt[A, CC[A] <: TraversableOnce[A]](coll: CC[A]) {
import collection.generic.CanBuildFrom
def distinctBy[B, That](f: A => B)(implicit cbf: CanBuildFrom[CC[A], A, That]): That = {
val b = cbf(coll)
val seen = collection.mutable.HashSet[B]()
for (x <- coll) {
val v = f(x)
if (!seen(v)) {
b += x
seen += v
}
}
b.result
}
}
implicit def commomExtendTraversable[A, C[A] <: TraversableOnce[A]](coll: C[A]): TraversableOnceExt[A, C] =
new TraversableOnceExt[A, C](coll)
答案 1 :(得分:2)
这是一个改进,将seen
放入折叠并通常清理(就像不构建集合只是为了向现有集合添加一个元素):
class EnrichedSeq[T](seq: Seq[T]) {
def distinctOn[A](f: T => A): Seq[T] = {
seq.foldLeft((Set[A](), Seq[T]())) {
case ((seen, res), curr) =>
val y = f(curr)
if (!seen(y))
(seen + y, res :+ curr)
else
(seen, res)
}._2
}
}
implicit def enrichSeq[T](self: Seq[T]) = new EnrichedSeq(self)
此外,您可以将其称为distinctBy
,因为这更符合库使用的命名约定(例如maxBy
,sortBy
等)