2012-04-13 3 views
2

Я пытаюсь реализовать функцию distinctOn для последовательности, которая будет принимать функцию f и возвращать последовательность, для которой каждый элемент имеет отличный результат, когда f применяется к нему. EG:Scala: реализация функции Seq.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 
     } 
    }} 
    } 
+0

почему бы не попробовать использовать 'groupBy' метод так: ' (. _ Имя) .map people.groupBy (_._ 2 (0)) ' – RyuuGan

+1

@RyuuGan, я думаю, что не будет сохранять приказ. –

+0

@RyuuGan, Пол прав, groupBy не сохраняет заказ. – ChucK

ответ

6

Вот implemen который сохраняет порядок там, где это применимо, а также работает для других Traversable s, чем Seq s. Он основан на реализации distinct и использует заводы-застройщики (a.k.a. CanBuildFrom s), используемые в других методах сбора.

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) 
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 и т.д.)

Смежные вопросы