2

我正在尝试为一个序列实现一个 distinctOn 函数,该函数将采用一个函数 f 并返回一个序列,当 f 应用于它时,每个项目都有一个不同的结果。例如:

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))

返回第一个副本 (Al),并保留顺序。我当前的实现包含一个 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
      }
    }}
  }
4

2 回答 2

6

这是一个实现,它在适用的情况下保留顺序,并且也适用于除Traversables 之外的其他Seqs。它基于其他收集方法中distinct使用的构建器工厂(又名)的实现并使用。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)
于 2012-04-13T07:37:40.813 回答
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等)

于 2012-04-13T07:27:44.273 回答