it-swarm.com.de

Wie finde ich Duplikate in einer Liste?

Ich habe eine Liste von unsortierten Ganzzahlen und möchte die Elemente finden, die Duplikate haben.

val dup = List(1,1,1,2,3,4,5,5,6,100,101,101,102)

Ich kann die verschiedenen Elemente des Sets mit dup.distinct finden, also habe ich meine Antwort wie folgt geschrieben.

val dup = List(1,1,1,2,3,4,5,5,6,100,101,101,102)
val distinct = dup.distinct
val elementsWithCounts = distinct.map( (a:Int) => (a, dup.count( (b:Int) => a == b )) )
val duplicatesRemoved = elementsWithCounts.filter( (pair: Pair[Int,Int]) => { pair._2 <= 1 } )
val withDuplicates = elementsWithCounts.filter( (pair: Pair[Int,Int]) => { pair._2 > 1 } )

Gibt es einen einfacheren Weg, dies zu lösen?

25
Phil

Versuche dies:

val dup = List(1,1,1,2,3,4,5,5,6,100,101,101,102)
dup.groupBy(identity).collect { case (x, List(_,_,_*)) => x }

Die Variable groupBy verknüpft jede eindeutige ganze Zahl mit einer Liste ihrer Vorkommen. Die collect ist im Grunde map, wobei nicht übereinstimmende Elemente ignoriert werden. Das Übereinstimmungsmuster nach case passt auf ganze Zahlen x, die einer Liste zugeordnet sind, die zum Muster List(_,_,_*) passt, einer Liste mit mindestens zwei Elementen, die jeweils durch einen Unterstrich dargestellt werden, da wir diese Werte (und diese beiden Elemente nicht wirklich speichern müssen) kann von null oder mehr Elementen gefolgt werden: _*).

Sie könnten auch tun:

dup.groupBy(identity).collect { case (x,ys) if ys.lengthCompare(1) > 0 => x }

Es ist viel schneller als der von Ihnen vorgeschlagene Ansatz, da er die Daten nicht wiederholt übergeben muss.

47
dhg

Ein bisschen zu spät zur Party, aber hier ist ein anderer Ansatz:

dup.diff(dup.distinct).distinct

diff gibt Ihnen alle zusätzlichen Elemente über denen im Argument (dup.distinct), die die Duplikate sind.

25
Luigi Plinge

Ein anderer Ansatz ist, foldLeft zu verwenden und es auf die harte Tour zu tun.

Wir beginnen mit zwei leeren Sets. Eines ist für Elemente, die wir mindestens einmal gesehen haben. Die andere für Elemente, die wir mindestens zweimal gesehen haben (auch bekannt als Duplikate). 

Wir gehen die Liste durch. Wenn das aktuelle Element bereits gesehen wurde (seen(cur)), ist es ein Duplikat und wird daher zu duplicates hinzugefügt. Andernfalls fügen wir es zu seen..__ hinzu. Das Ergebnis ist jetzt die zweite Menge, die die Duplikate enthält.

Wir können dies auch als generische Methode schreiben.

def dups[T](list: List[T]) = list.foldLeft((Set.empty[T], Set.empty[T])){ case ((seen, duplicates), cur) => 
  if(seen(cur)) (seen, duplicates + cur) else (seen + cur, duplicates)      
}._2

val dup = List(1,1,1,2,3,4,5,5,6,100,101,101,102)

dups(dup) //Set(1,5,101)
2
Kigyo

Summary: Ich habe eine sehr effiziente Funktion geschrieben, die sowohl List.distinct als auch eine List zurückgibt, die aus jedem Element besteht, das mehr als einmal auftrat, und dem Index, an dem das Elementduplikat erschien.

Details: Wenn Sie, wie ich, etwas mehr Informationen über die Duplikate selbst benötigen, habe ich eine allgemeinere Funktion geschrieben, die genau einmal eine List durchläuft (da die Reihenfolge signifikant war) und einen Tuple2-Wert zurückgibt der ursprünglichen List deduped (alle Duplikate nach dem ersten werden entfernt; dh das gleiche wie der Aufruf von distinct) und eine zweite List, die jedes Duplikat zeigt, und einen Int-Index, bei dem es innerhalb der ursprünglichen List aufgetreten ist.

Ich habe die Funktion zweimal implementiert, basierend auf den allgemeinen Leistungsmerkmalen der Scala-Sammlungen ; filterDupesL (wobei L für linear steht) und filterDupesEc (wo Ec für effektiv konstant ist).

Hier ist die Funktion "Linear":

def filterDupesL[A](items: List[A]): (List[A], List[(A, Int)]) = {
  def recursive(
      remaining: List[A]
    , index: Int =
        0
    , accumulator: (List[A], List[(A, Int)]) =
        (Nil, Nil)): (List[A], List[(A, Int)]
  ) =
    if (remaining.isEmpty)
      accumulator
    else
      recursive(
          remaining.tail
        , index + 1
        , if (accumulator._1.contains(remaining.head)) //contains is linear
          (accumulator._1, (remaining.head, index) :: accumulator._2)
        else
          (remaining.head :: accumulator._1, accumulator._2)
      )
  val (distinct, dupes) = recursive(items)
  (distinct.reverse, dupes.reverse)
}

Ein Beispiel ist ein Beispiel, das es vielleicht etwas intuitiver macht. Diese Liste der String-Werte gegeben:

val withDupes =
  List("a.b", "a.c", "b.a", "b.b", "a.c", "c.a", "a.c", "d.b", "a.b")

... und dann folgendes ausführen:

val (deduped, dupeAndIndexs) =
  filterDupesL(withDupes)

... die Ergebnisse sind:

deduped: List[String] = List(a.b, a.c, b.a, b.b, c.a, d.b)
dupeAndIndexs: List[(String, Int)] = List((a.c,4), (a.c,6), (a.b,8))

Und wenn Sie nur die Duplikate haben möchten, map über dupeAndIndexes und rufen Sie distinct auf:

val dupesOnly =
  dupeAndIndexs.map(_._1).distinct

... oder alles in einem Anruf:

val dupesOnly =
  filterDupesL(withDupes)._2.map(_._1).distinct

... oder wenn eine Set bevorzugt wird, überspringen Sie distinct und rufen Sie toSet... auf.

val dupesOnly2 =
  dupeAndIndexs.map(_._1).toSet

... oder alles in einem Anruf:

val dupesOnly2 =
  filterDupesL(withDupes)._2.map(_._1).toSet

Für sehr große Lists sollten Sie die Verwendung dieser effizienteren Version in Betracht ziehen (die eine zusätzliche Set verwendet, um die contains-Prüfung in effektiv konstanter Zeit zu ändern):

Hier ist die Funktion "effektiv konstant":

def filterDupesEc[A](items: List[A]): (List[A], List[(A, Int)]) = {
  def recursive(
      remaining: List[A]
    , index: Int =
        0
    , seenAs: Set[A] =
        Set()
    , accumulator: (List[A], List[(A, Int)]) =
        (Nil, Nil)): (List[A], List[(A, Int)]
  ) =
    if (remaining.isEmpty)
      accumulator
    else {
      val (isInSeenAs, seenAsNext) = {
        val isInSeenA =
          seenAs.contains(remaining.head) //contains is effectively constant
        (
            isInSeenA
          , if (!isInSeenA)
              seenAs + remaining.head
            else
              seenAs
        )
      }
      recursive(
          remaining.tail
        , index + 1
        , seenAsNext
        , if (isInSeenAs)
          (accumulator._1, (remaining.head, index) :: accumulator._2)
        else
          (remaining.head :: accumulator._1, accumulator._2)
      )
    }
  val (distinct, dupes) = recursive(items)
  (distinct.reverse, dupes.reverse)
}

Bei beiden Funktionen handelt es sich um Anpassungen der Funktion filterDupes in meiner Open-Source-Scala-Bibliothek ScalaOlio . Es befindet sich bei org.scalaolio.collection.immutable.List_._.

2

Mein Liebling ist 

def hasDuplicates(in: List[Int]): Boolean = {
  val sorted = in.sortWith((i, j) => i < j)
  val zipped = sorted.tail.Zip(sorted)
  zipped.exists(p => p._1 == p._2)
}
0
user8221989