Scala:Seq.distinctOn函数的实现

时间:2012-04-13 07:19:50

标签: scala scala-collections

我正在尝试为一个序列实现一个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
      }
    }}
  }

2 个答案:

答案 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,因为这更符合库使用的命名约定(例如maxBysortBy等)