2

関数fを取り、fが適用されたときに各アイテムの結果が異なるシーケンスを返すシーケンスに対して、distinctOn関数を実装しようとしています。例えば:

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以外でも機能する実装Seqです。これは、他の収集方法で使用されるdistinctビルダーファクトリ(別名s)の実装に基づいており、それらを使用します。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(既存のコレクションに1つの要素を追加するためだけにコレクションを構築しないなど)。

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 に答える