Entfernen doppelter Elemente aus einem Array in Swift


252

Heutzutage geben Sie in Swift einfach ein Set( yourArray ), um ein Array einzigartig zu machen. (Oder ein bestelltes Set, falls erforderlich.)

Wie wurde es gemacht, bevor das möglich war?


Ich könnte ein Array haben, das wie folgt aussieht:

[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]

Oder wirklich jede Folge von gleichartigen Teilen von Daten. Ich möchte sicherstellen, dass es nur eines von jedem identischen Element gibt. Zum Beispiel würde das obige Array werden:

[1, 4, 2, 6, 24, 15, 60]

Beachten Sie, dass die Duplikate von 2, 6 und 15 entfernt wurden, um sicherzustellen, dass nur eines von jedem identischen Element vorhanden ist. Bietet Swift eine Möglichkeit, dies einfach zu tun, oder muss ich es selbst tun?


11
Am einfachsten ist es, das Array in ein NSSetNSSet zu konvertieren. NSSet ist eine ungeordnete Sammlung von Objekten, wenn die Reihenfolge NSOrderedSet beibehalten werden muss.
Andrea

Sie können die Schnittfunktion verwenden, wie Sie sie in dieser Klasse mit Funktionen für Arrays finden: github.com/pNre/ExSwift/blob/master/ExSwift/Array.swift
Edwin Vermeer

Nicht Teil von Swift, aber ich benutze Dollar. $.uniq(array) github.com/ankurp/Dollar#uniq---uniq
Andy

Die wahrscheinlich eleganteste, intelligenteste und schnellste Antwort liefert die Antwort von mxcl unten. Das hilft auch, die Ordnung aufrechtzuerhalten
Honey

1
Warum benutzt du nicht einfach SetSwift? Sie können eine Liste ungeordneter und eindeutiger Elemente bereitstellen.
TibiaZ

Antworten:


133

Sie können Ihre eigenen rollen, zB wie folgt ( aktualisiert für Swift 1.2 mit Set ):

func uniq<S : SequenceType, T : Hashable where S.Generator.Element == T>(source: S) -> [T] {
    var buffer = [T]()
    var added = Set<T>()
    for elem in source {
        if !added.contains(elem) {
            buffer.append(elem)
            added.insert(elem)
        }
    }
    return buffer
}

let vals = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let uniqueVals = uniq(vals) // [1, 4, 2, 6, 24, 15, 60]

Swift 3 Version:

func uniq<S : Sequence, T : Hashable>(source: S) -> [T] where S.Iterator.Element == T {
    var buffer = [T]()
    var added = Set<T>()
    for elem in source {
        if !added.contains(elem) {
            buffer.append(elem)
            added.insert(elem)
        }
    }
    return buffer
}

Und als Erweiterung für Array:

extension Array where Element: Hashable {
    var uniques: Array {
        var buffer = Array()
        var added = Set<Element>()
        for elem in self {
            if !added.contains(elem) {
                buffer.append(elem)
                added.insert(elem)
            }
        }
        return buffer
    }
}

12
Sie können den Hauptteil dieser Funktion auch alsvar addedDict = [T:Bool](); return filter(source) { addedDict(true, forKey: $0) == nil }
Airspeed Velocity

1
@ AirspeedVelocity: Meinten Sie updateValue(true, forKey: $0)...stattaddedDict(true, forKey: $0)...
Jawwad

1
Ups ja sorry ich habe versehentlich die Methode! Sollte so sein return filter(source) { addedDict.updateValue(true, forKey: $0) == nil }wie du sagst.
Fluggeschwindigkeit

21
Nur ein Wort der Vorsicht: Vermeiden Sie es, die Leistung für einfache Funktionen wie diese zu diskutieren, bis Sie nachweislich von ihrer Leistung abhängig sind. An diesem Punkt sollten Sie nur noch einen Benchmark durchführen. Zu oft habe ich aufgrund von Annahmen nicht wartbaren Code oder sogar weniger performanten Code gesehen. :) Auch das ist wahrscheinlich leichter zu verstehen:let uniques = Array(Set(vals))
Blixt

11
@Blixt Einverstanden. Auch hier liegt der Vorteil darin, die Reihenfolge der Elemente des ursprünglichen Arrays zu berücksichtigen.
Jean-Philippe Pellet

493

Sie können ganz einfach in ein Set und wieder in ein Array konvertieren:

let unique = Array(Set(originals))

Es wird nicht garantiert, dass die ursprüngliche Reihenfolge des Arrays beibehalten wird.


37
Gibt es eine Möglichkeit, einen Satz zu verwenden, während die ursprüngliche Reihenfolge des Arrays beibehalten wird?
Crashalot

6
@Crashalot Siehe meine Antwort.
Jean-Philippe Pellet

5
Wenn Sie die Objekte durch eine bestimmte Eigenschaft eindeutig halten müssen, implementieren Sie auch das Hashable- und Equatable-Protokoll für diese Klasse, anstatt nur die Array-> Set-> Array-Transformation zu verwenden
Fawkes

2
Nett!! Was ist die zeitliche Komplexität dieser Lösung bitte?
JW.ZG

2
Schlägt fehl, wenn die Elemente in originalsnicht vorhanden sind Hashable. HashableEinem Set können nur Datentypen hinzugefügt werden, einem Array kann jedoch jeder Datentyp hinzugefügt werden.
Mecki

69

Viele Antworten finden Sie hier, aber ich habe diese einfache Erweiterung verpasst, die für Swift 2 und höher geeignet ist:

extension Array where Element:Equatable {
    func removeDuplicates() -> [Element] {
        var result = [Element]()

        for value in self {
            if result.contains(value) == false {
                result.append(value)
            }
        }

        return result
    }
}

Macht es super einfach. Kann so genannt werden:

let arrayOfInts = [2, 2, 4, 4]
print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]

Filterung basierend auf Eigenschaften

Um ein Array basierend auf Eigenschaften zu filtern, können Sie folgende Methode verwenden:

extension Array {

    func filterDuplicates(@noescape includeElement: (lhs:Element, rhs:Element) -> Bool) -> [Element]{
        var results = [Element]()

        forEach { (element) in
            let existingElements = results.filter {
                return includeElement(lhs: element, rhs: $0)
            }
            if existingElements.count == 0 {
                results.append(element)
            }
        }

        return results
    }
}

Was Sie wie folgt anrufen können:

let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }

@Antoine Vielen Dank für die Filterung basierend auf der Eigenschaftenerweiterung. Es ist wirklich nützlich. Aber können Sie bitte erklären, wie es funktioniert. Es ist zu schwer für mich zu verstehen. Vielen Dank
Mostafa Mohamed Raafat

Updates für Swift 3: func filterDuplicates (_ includeElement: (_ lhs: Element, _ rhs: Element) -> Bool) -> [Element] {
cbartel

Der erste Teil dieser Antwort ( extension Array where Element: Equatable) wird durch stackoverflow.com/a/36048862/1033581 ersetzt, das eine leistungsfähigere Lösung bietet ( extension Sequence where Iterator.Element: Equatable).
Cœur

7
Dies hat eine O(n²)zeitliche Leistung, was für große Arrays sehr schlecht ist.
Duncan C

Sie sollten ein Set verwenden, um die bisher gesehenen Elemente im Auge zu behalten und diese schreckliche O(n²)Komplexität wieder aufO(n)
Alexander - Reinstate Monica

63

Swift 3.0

let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))

1
Lassen Sie uniqueOrderedNames = Array (NSOrderedSet (Array: Benutzernamen)) als! [String] wenn Sie Array von String haben, nicht von irgendwelchen
Zaporozhchenko Oleksandr

Schlägt fehl, wenn die Elemente in arraynicht vorhanden sind Hashable. HashableEinem Set können nur Datentypen hinzugefügt werden, einem Array kann jedoch jeder Datentyp hinzugefügt werden.
Mecki

Getestet in Swift 5.1b5, da die Elemente Hash-fähig sind und die Reihenfolge beibehalten werden soll, ist das NSOrderedSet (Array: Array) .array geringfügig schneller als das reine Swift Func Uniqued () unter Verwendung eines Sets mit Filter. Ich habe mit 5100 Zeichenfolgen getestet, was zu 13 eindeutigen Werten führte.
Dlemex

62

Wenn Sie beide Erweiterungen in Ihren Code einfügen, Hashablewird nach Möglichkeit die schnellere Version und die EquatableVersion als Fallback verwendet.

public extension Sequence where Element: Hashable {
  var firstUniqueElements: [Element] {
    var set: Set<Element> = []
    return filter { set.insert($0).inserted }
  }
}

public extension Sequence where Element: Equatable {
  var firstUniqueElements: [Element] {
    reduce(into: []) { uniqueElements, element in
      if !uniqueElements.contains(element) {
        uniqueElements.append(element)
      }
    }
  }
}

Wenn die Reihenfolge nicht wichtig ist, können Sie immer nur diesen Set-Initialisierer verwenden .


Okay, verstanden. Ich konnte es nicht aufrufen, weil mein Array ein Array von Strukturen ist ... wie würde ich in meinem Fall damit umgehen? Struktur von 20 verschiedenen Variablen, String und [String]
David Seek

@ David Seek Es hört sich so an, als hätten Sie Ihren strengen Hash nicht oder nicht gleichwertig gemacht. Ist das korrekt?
Jessy

1
@ DavidSeek wie folgt, uniqueArray = nonUniqueArray.uniqueElements
Mert Celik

Ja, mach dir keine Sorgen. habe es gleich danach zum Laufen gebracht. seit fast 2 Jahren: P
David Seek

Dies hat eine O(n²)zeitliche Leistung, was für große Arrays sehr schlecht ist.
Duncan C

44

Swift 4 oder höher bearbeiten / aktualisieren

Wir können das RangeReplaceableCollectionProtokoll auch erweitern , damit es auch mit StringProtocolTypen verwendet werden kann:

extension RangeReplaceableCollection where Element: Hashable {
    var orderedSet: Self {
        var set = Set<Element>()
        return filter { set.insert($0).inserted }
    }
    mutating func removeDuplicates() {
        var set = Set<Element>()
        removeAll { !set.insert($0).inserted }
    }
}

let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]

"abcdefabcghi".orderedSet  // "abcdefghi"
"abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"

Mutationsmethode:

var string = "abcdefabcghi"
string.removeDuplicates() 
string  //  "abcdefghi"

var substring = "abcdefabcdefghi".dropFirst(3)  // "defabcdefghi"
substring.removeDuplicates()
substring   // "defabcghi"

Für Swift 3 klicken Sie hier


1
Ich mag das, es funktioniert auch mit einer Reihe von Wörterbüchern!
DeyaEldeen

6
O (N ^ 2) ist schlecht :(
Alexander - Reinstate Monica

1
@Alexander Leo Dabus hat die reduceImplementierung ersetzt, daher ist die Komplexität jetzt anders.
Cœur

1
Die Ergebnisse sind interessant. Sowohl für 1 Million Einzelstücke als auch für 8 Millionen ist die Filterversion schneller. Die filterbasierte Version benötigt jedoch 8,38-mal länger für 8 Millionen eindeutige Elemente (ein Haar im Laufe der O(n)Zeit), während die Flatmap-basierte Version für 8 Millionen eindeutige Einträge 7,47-mal länger dauert als 1 Million, was darauf hindeutet, dass die Flatmap-basierte Version besser skaliert . Irgendwie ist die Flatmap-basierte Version etwas besser als die O(n)Zeit!
Duncan C

1
Wenn ich den Test mit 64x mehr Elementen im Array ausführe, ist die Flatmap-basierte Version sogar schneller.
Duncan C

43

Swift 4

public extension Array where Element: Hashable {
    func uniqued() -> [Element] {
        var seen = Set<Element>()
        return filter{ seen.insert($0).inserted }
    }
}

Bei jedem Versuch insertwird auch ein Tupel zurückgegeben : (inserted: Bool, memberAfterInsert: Set.Element). Siehe Dokumentation .

Die Verwendung des zurückgegebenen Werts hilft uns, Schleifen oder andere Operationen zu vermeiden.


7
Nach einer einfachen Profilerstellung ist diese Methode sehr schnell. Es ist hunderte Male schneller als mit Reduzieren (_: _ :) oder sogar Reduzieren (in: _ :)
Kelvin

3
@ Kelvin Weil all diese anderen Algorithmen waren O(n^2)und niemand es bemerkte.
Alexander - Reinstate Monica

@ Kelvin diese Antwort ist identisch mit Eneko Alonso Antwort + mein Kommentar (16. Juni 17).
Cœur

27

Swift 4

Garantiert weiterhin zu bestellen.

extension Array where Element: Equatable {
    func removingDuplicates() -> Array {
        return reduce(into: []) { result, element in
            if !result.contains(element) {
                result.append(element)
            }
        }
    }
}

Ich benutze dies jetzt, änderte nur den Methodennamen in removeDuplicates :)
J. Doe

Ich denke, diese Lösung ist kompakt, aber ich glaube, dass die DeanWombourne-Lösung, die ein Jahr zuvor veröffentlicht wurde, etwas effizienter sein kann als a reduce: Insgesamt ist es nur eine weitere Zeile in Ihrem gesamten Projekt, Ihre Funktion wie folgt zu schreiben : var unique: [Iterator.Element] = []; for element in self where !unique.contains(element) { unique.append(element) }; return unique. Ich gebe zu, ich habe die relativen Leistungen noch nicht getestet.
Cœur

3
Dies hat eine O(n²)zeitliche Leistung, was für große Arrays sehr schlecht ist.
Duncan C

@ NickGaens Nein, ist es nicht, es ist O(n²). Daran ist nichts schnelles.
Alexander - Reinstate Monica

@ Cœur reduceoder reduce(into:)würde keinen kritischen Unterschied machen. Das Umschreiben, um nicht wiederholt anzurufen, containswürde einen VIEL größeren Unterschied machen.
Alexander - Reinstate Monica

16

Hier ist eine Kategorie auf SequenceTypedem die ursprüngliche Reihenfolge der Anordnung bewahrt, sondern verwendet ein Setdie tun containsLookups das zu vermeiden , O(n)Kosten auf Array der contains(_:)Methode.

public extension Sequence where Element: Hashable {

    /// Return the sequence with all duplicates removed.
    ///
    /// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234, as 
    ///         per @Alexander's comment.
    func uniqued() -> [Element] {
        var seen = Set<Element>()
        return self.filter { seen.insert($0).inserted }
    }
}

Wenn Sie nicht Hashable oder Equatable sind, können Sie ein Prädikat übergeben, um die Gleichheitsprüfung durchzuführen:

extension Sequence {

    /// Return the sequence with all duplicates removed.
    ///
    /// Duplicate, in this case, is defined as returning `true` from `comparator`.
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234
    func uniqued(comparator: @escaping (Element, Element) throws -> Bool) rethrows -> [Element] {
        var buffer: [Element] = []

        for element in self {
            // If element is already in buffer, skip to the next element
            if try buffer.contains(where: { try comparator(element, $0) }) {
                continue
            }

            buffer.append(element)
        }

        return buffer
    }
}

Nun, wenn Sie nicht Hashable haben, aber sind gleichzusetzen, können Sie diese Methode verwenden:

extension Sequence where Element: Equatable {

    /// Return the sequence with all duplicates removed.
    ///
    /// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234
    func uniqued() -> [Element] {
        return self.uniqued(comparator: ==)
    }
}

Schließlich können Sie eine Schlüsselpfadversion von Uniqued wie folgt hinzufügen:

extension Sequence {

    /// Returns the sequence with duplicate elements removed, performing the comparison usinig the property at
    /// the supplied keypath.
    ///
    /// i.e.
    ///
    /// ```
    /// [
    ///   MyStruct(value: "Hello"),
    ///   MyStruct(value: "Hello"),
    ///   MyStruct(value: "World")
    ///  ].uniqued(\.value)
    /// ```
    /// would result in
    ///
    /// ```
    /// [
    ///   MyStruct(value: "Hello"),
    ///   MyStruct(value: "World")
    /// ]
    /// ```
    ///
    /// - note: Taken from stackoverflow.com/a/46354989/3141234
    ///
    func uniqued<T: Equatable>(_ keyPath: KeyPath<Element, T>) -> [Element] {
        self.uniqued { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
    }
}

Sie können beide in Ihre App einfügen. Swift wählt je nach Sequenztyp die richtige aus Iterator.Element.


Heyyy endlich jemand mit einer O(n)Lösung. Sie können die Set-Operationen "check" und "insert" übrigens zu einer kombinieren. Siehe stackoverflow.com/a/46354989/3141234
Alexander - Reinstate Monica

Oh, das ist klug :)
DeanWombourne

14

Inspiriert von https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift können wir ein leistungsfähigeres Tool deklarieren, das auf jedem keyPath nach Einheitlichkeit filtern kann. Dank der Kommentare von Alexander zu verschiedenen Antworten bezüglich der Komplexität sollten die folgenden Lösungen nahezu optimal sein.

Nicht mutierende Lösung

Wir erweitern mit einer Funktion, die in der Lage ist, auf jedem keyPath nach Einigkeit zu filtern:

extension RangeReplaceableCollection {
    /// Returns a collection containing, in order, the first instances of
    /// elements of the sequence that compare equally for the keyPath.
    func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> Self {
        var unique = Set<T>()
        return filter { unique.insert($0[keyPath: keyPath]).inserted }
    }
}

Hinweis: Wenn Ihr Objekt nicht RangeReplaceableCollection, sondern Sequence entspricht, können Sie diese zusätzliche Erweiterung verwenden. Der Rückgabetyp ist jedoch immer ein Array:

extension Sequence {
    /// Returns an array containing, in order, the first instances of
    /// elements of the sequence that compare equally for the keyPath.
    func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> [Element] {
        var unique = Set<T>()
        return filter { unique.insert($0[keyPath: keyPath]).inserted }
    }
}

Verwendung

Wenn wir wie in der Frage eine Einheit für die Elemente selbst wünschen, verwenden wir den keyPath \.self:

let a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let b = a.unique(for: \.self)
/* b is [1, 4, 2, 6, 24, 15, 60] */

Wenn wir Einheit für etwas anderes wollen (wie für die ideine Sammlung von Objekten), verwenden wir den Schlüsselpfad unserer Wahl:

let a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
let b = a.unique(for: \.y)
/* b is [{x 1 y 1}, {x 1 y 2}] */

Mutierende Lösung

Wir erweitern mit einer Mutationsfunktion, die in der Lage ist, auf jedem keyPath nach Einigkeit zu filtern:

extension RangeReplaceableCollection {
    /// Keeps only, in order, the first instances of
    /// elements of the collection that compare equally for the keyPath.
    mutating func uniqueInPlace<T: Hashable>(for keyPath: KeyPath<Element, T>) {
        var unique = Set<T>()
        removeAll { !unique.insert($0[keyPath: keyPath]).inserted }
    }
}

Verwendung

Wenn wir wie in der Frage eine Einheit für die Elemente selbst wünschen, verwenden wir den keyPath \.self:

var a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
a.uniqueInPlace(for: \.self)
/* a is [1, 4, 2, 6, 24, 15, 60] */

Wenn wir die Einheitlichkeit für etwas anderes wollen (wie für ideine Sammlung von Objekten), verwenden wir den Schlüsselpfad unserer Wahl:

var a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
a.uniqueInPlace(for: \.y)
/* a is [{x 1 y 1}, {x 1 y 2}] */

1
Das ist eine gute Implementierung! Ich nur mit diesen Schlüsselpfaden konnte in Closures konvertiert werden, so dass Sie ein Closure-Argument verwenden können, um sowohl beliebigen Code (in Closures) als auch bloße Eigenschaftssuchen (über Schlüsselpfade) zu unterstützen. Die einzige Änderung, die ich vornehmen würde, ist die keyPathStandardeinstellung \.self, da dies wahrscheinlich die Mehrheit der Anwendungsfälle ist.
Alexander - Reinstate Monica

1
@Alexander Ich habe versucht, standardmäßig auf Self zu setzen, aber dann müsste ich Elementimmer machen Hashable. Eine Alternative zu einem Standardwert ist das Hinzufügen einer einfachen Überladung ohne Parameter:extension Sequence where Element: Hashable { func unique() { ... } }
Cœur

Ah ja, macht Sinn!
Alexander - Reinstate Monica

1
Genial ... einfach und am besten "flexibel". Vielen Dank.
BonanzaDriver

12

Eine alternative (wenn nicht optimale) Lösung von hier aus , bei der unveränderliche Typen anstelle von Variablen verwendet werden:

func deleteDuplicates<S: ExtensibleCollectionType where S.Generator.Element: Equatable>(seq:S)-> S {
    let s = reduce(seq, S()){
        ac, x in contains(ac,x) ? ac : ac + [x]
    }
    return s
}

Eingeschlossen, um Jean-Pillippes imperativen Ansatz einem funktionalen Ansatz gegenüberzustellen.

Als Bonus funktioniert diese Funktion sowohl mit Strings als auch mit Arrays!

Bearbeiten: Diese Antwort wurde 2014 für Swift 1.0 geschrieben (zuvor Setwar sie in Swift verfügbar). Es erfordert keine Hashable-Konformität und wird in quadratischer Zeit ausgeführt.


8
Beachten Sie, dass es nicht eine, sondern zwei Möglichkeiten gibt, wie dies in quadratischer Zeit ausgeführt wird - beide containsund Array-Anhänge werden in O (n) ausgeführt. Obwohl es den Vorteil hat, nur gleichwertig zu sein, nicht hashbar.
Fluggeschwindigkeit

Dies ist eine wirklich komplizierte Art zu schreiben filter. Es ist O (n ^ 2) (was erforderlich ist, wenn Sie keine HashableKonformität benötigen ), aber Sie sollten dies zumindest explizit
ausrufen

10

schnell 2

mit uniq funktion antwort :

func uniq<S: SequenceType, E: Hashable where E==S.Generator.Element>(source: S) -> [E] {
    var seen: [E:Bool] = [:]
    return source.filter({ (v) -> Bool in
        return seen.updateValue(true, forKey: v) == nil
    })
}

verwenden:

var test = [1,2,3,4,5,6,7,8,9,9,9,9,9,9]
print(uniq(test)) //1,2,3,4,5,6,7,8,9

Der BoolWert ist offensichtlich redundant, da Ihr Code ihn nie liest. Verwenden Sie a Setanstelle von a Dictionaryund Sie erhalten meine positive Bewertung.
Nikolai Ruhe

10

In Swift 5

 var array: [String] =  ["Aman", "Sumit", "Aman", "Sumit", "Mohan", "Mohan", "Amit"]

 let uniq = Array(Set(array))
 print(uniq)

Ausgabe wird sein

 ["Sumit", "Mohan", "Amit", "Aman"]

2
Dies ist eine Wiederholung vieler Antworten, die bereits hier vorliegen, und die Reihenfolge bleibt nicht erhalten.
Alexander - Monica

9

Eine weitere Swift 3.0-Lösung zum Entfernen von Duplikaten aus einem Array. Diese Lösung verbessert viele andere Lösungen, die bereits vorgeschlagen wurden von:

  • Beibehaltung der Reihenfolge der Elemente im Eingabearray
  • Lineare Komplexität O (n): Single-Pass-Filter O (n) + Set Insertion O (1)

Angesichts des Integer-Arrays:

let numberArray = [10, 1, 2, 3, 2, 1, 15, 4, 5, 6, 7, 3, 2, 12, 2, 5, 5, 6, 10, 7, 8, 3, 3, 45, 5, 15, 6, 7, 8, 7]

Funktionscode:

func orderedSet<T: Hashable>(array: Array<T>) -> Array<T> {
    var unique = Set<T>()
    return array.filter { element in
        return unique.insert(element).inserted
    }
}

orderedSet(array: numberArray)  // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]

Array-Erweiterungscode:

extension Array where Element:Hashable {
    var orderedSet: Array {
        var unique = Set<Element>()
        return filter { element in
            return unique.insert(element).inserted
        }
    }
}

numberArray.orderedSet // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]

Dieser Code nutzt das Ergebnis, das von der insertOperation on zurückgegeben wird Set, die on ausgeführt wirdO(1) , und gibt ein Tupel zurück, das angibt, ob das Element eingefügt wurde oder ob es bereits in der Gruppe vorhanden war.

Wenn sich das Element im Set befand, filterwird es vom Endergebnis ausgeschlossen.


1
Um nicht wählerisch zu sein, führen Sie die Einfügung und den Mitgliedschaftstest so oft durch, wie es Elemente gibt. Daher sollten Sie deren Kosten ebenfalls als O (n) zählen. Dies bedeutet jedoch nicht 3xO (n), da diese O's nicht die gleichen Kosten wie der Filter haben und die Zugabe von O (n) Äpfeln zu Orangen ist. Wenn wir Mengenoperationen als einen O (1) -Teil der Filterkosten betrachten, ist die Komplexität lediglich O (n), wenn auch mit einem größeren "O". Wenn Sie dies an die Grenze treiben, können Sie auch die Einfügungen vermeiden, wenn sich das Element bereits im Satz befindet.
Alain T.

Sie haben Recht, deferwenn Sie den Code verwenden, wird die eingestellte Testoperation zweimal ausgeführt, einmal mit containsund einmal mit insert. Beim Lesen der Swift-Dokumentation stellte ich fest, dass insertein Tupel zurückgegeben wird, das angibt, ob das Element eingefügt wurde oder nicht. Daher habe ich den Code zum Entfernen der containsPrüfung vereinfacht .
Eneko Alonso

2
Nett. Ihre Erweiterung , indem sie es auf sein optimales könnteextension Sequence where Iterator.Element: Hashable { ... }
Cœur

@AlainT. Nee. Beides insertund containshaben O(1)Komplexität. O(1) + O(1) = O(1). Diese beiden Operationen werden dann nmal ausgeführt (einmal pro Aufruf des an übergebenen Abschlusses filter, der einmal pro Element aufgerufen wird). Wenn eine Operation unabhängig von der Eingabegröße eine konstante Zeit benötigt, dauert die zweimalige Ausführung immer noch eine konstante Zeit das ist unabhängig von der Eingabegröße. Die Gesamtkomplexität davon ist O(n).
Alexander - Reinstate Monica

9

Swift 4.x:

extension Sequence where Iterator.Element: Hashable {
  func unique() -> [Iterator.Element] {
    return Array(Set<Iterator.Element>(self))
  }

  func uniqueOrdered() -> [Iterator.Element] {
    return reduce([Iterator.Element]()) { $0.contains($1) ? $0 : $0 + [$1] }
  }
}

Verwendung:

["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()

oder

["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()

Das ist O(n^2). Tu das nicht.
Alexander - Reinstate Monica

8

Swift 5

extension Sequence where Element: Hashable {
    func unique() -> [Element] {
        NSOrderedSet(array: self as! [Any]).array as! [Element]
    }
}

Ich habe einige Variationen vorgenommen, damit ich einen Schlüssel zum Vergleichen auswählen kann. extension Sequence { // Returns distinct elements based on a key value. func distinct<key: Hashable>(by: ((_ el: Iterator.Element) -> key)) -> [Iterator.Element] { var existing = Set<key>() return self.filter { existing.insert(by($0)).inserted } } }
Marcelo de Aguiar

Es ist nicht erforderlich, a zu verwenden Bool, wenn der einzige Wert, den Sie verwenden, ist true. Sie greifen nach einem "Einheitentyp" (einem Typ mit nur einem möglichen Wert). Swifts Einheitentyp ist Void, dessen einziger Wert ()(auch bekannt als leeres Tupel) ist. Sie können also einfach verwenden [T: Void]. Obwohl Sie das nicht tun sollten, weil Sie im Grunde nur erfunden haben Set. Verwenden Sie Setstattdessen. Siehe stackoverflow.com/a/55684308/3141234 Bitte löschen Sie diese Antwort.
Alexander - Monica

8

Denken Sie wie ein funktionierender Programmierer :)

Um die Liste danach zu filtern, ob das Element bereits aufgetreten ist, benötigen Sie den Index. Sie können verwenden enumerated, um den Index abzurufen und mapzur Werteliste zurückzukehren.

let unique = myArray
    .enumerated()
    .filter{ myArray.firstIndex(of: $0.1) == $0.0 }
    .map{ $0.1 }

Dies garantiert die Bestellung. Wenn Ihnen die Reihenfolge nichts ausmacht, ist die vorhandene Antwort von Array(Set(myArray))einfacher und wahrscheinlich effizienter.


UPDATE: Einige Hinweise zu Effizienz und Korrektheit

Einige Leute haben die Effizienz kommentiert. Ich bin definitiv in der Schule, zuerst korrekten und einfachen Code zu schreiben und später Engpässe herauszufinden, obwohl ich es zu schätzen weiß, dass es fraglich ist, ob dies klarer ist alsArray(Set(array)) .

Diese Methode ist viel langsamer als Array(Set(array)) . Wie in den Kommentaren erwähnt, bleibt die Reihenfolge erhalten und es werden Elemente verarbeitet, die nicht haschbar sind.

Die Methode von @Alain T bewahrt jedoch auch die Ordnung und ist auch viel schneller. Wenn Ihr Elementtyp also nicht hashbar ist oder Sie nur einen schnellen Einzeiler benötigen, würde ich vorschlagen, mit ihrer Lösung fortzufahren.

Hier sind einige Tests auf einem MacBook Pro (2014) unter Xcode 11.3.1 (Swift 5.1) im Release-Modus.

Die Profiler-Funktion und zwei Methoden zum Vergleichen:

func printTimeElapsed(title:String, operation:()->()) {
    var totalTime = 0.0
    for _ in (0..<1000) {
        let startTime = CFAbsoluteTimeGetCurrent()
        operation()
        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
        totalTime += timeElapsed
    }
    let meanTime = totalTime / 1000
    print("Mean time for \(title): \(meanTime) s")
}

func method1<T: Hashable>(_ array: Array<T>) -> Array<T> {
    return Array(Set(array))
}

func method2<T: Equatable>(_ array: Array<T>) -> Array<T>{
    return array
    .enumerated()
    .filter{ array.firstIndex(of: $0.1) == $0.0 }
    .map{ $0.1 }
}

// Alain T.'s answer (adapted)
func method3<T: Hashable>(_ array: Array<T>) -> Array<T> {
    var uniqueKeys = Set<T>()
    return array.filter{uniqueKeys.insert($0).inserted}
}

Und eine kleine Auswahl an Testeingaben:

func randomString(_ length: Int) -> String {
  let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  return String((0..<length).map{ _ in letters.randomElement()! })
}

let shortIntList = (0..<100).map{_ in Int.random(in: 0..<100) }
let longIntList = (0..<10000).map{_ in Int.random(in: 0..<10000) }
let longIntListManyRepetitions = (0..<10000).map{_ in Int.random(in: 0..<100) }
let longStringList = (0..<10000).map{_ in randomString(1000)}
let longMegaStringList = (0..<10000).map{_ in randomString(10000)}

Gibt als Ausgabe:

Mean time for method1 on shortIntList: 2.7358531951904296e-06 s
Mean time for method2 on shortIntList: 4.910230636596679e-06 s
Mean time for method3 on shortIntList: 6.417632102966309e-06 s
Mean time for method1 on longIntList: 0.0002518167495727539 s
Mean time for method2 on longIntList: 0.021718120217323302 s
Mean time for method3 on longIntList: 0.0005312927961349487 s
Mean time for method1 on longIntListManyRepetitions: 0.00014377200603485108 s
Mean time for method2 on longIntListManyRepetitions: 0.0007293639183044434 s
Mean time for method3 on longIntListManyRepetitions: 0.0001843773126602173 s
Mean time for method1 on longStringList: 0.007168249964714051 s
Mean time for method2 on longStringList: 0.9114790915250778 s
Mean time for method3 on longStringList: 0.015888616919517515 s
Mean time for method1 on longMegaStringList: 0.0525397013425827 s
Mean time for method2 on longMegaStringList: 1.111266262292862 s
Mean time for method3 on longMegaStringList: 0.11214958941936493 s

1
Im Gegensatz Array(Set(myArray))dazu funktioniert dies für Dinge, die es nicht sindHashable
Porter Child

1
... und im Gegensatz Array(Set(myArray))zur Reihenfolge Ihres Arrays wird beibehalten.
Sander Saelmans

Es scheint mir die beste Antwort zu sein, zumindest derzeit, wenn Swift 5 bereits die aktuelle Version ist.
Oradyvan

Dies ist eine sehr elegante Lösung; leider ist es auch ziemlich langsam.
Colin Stark

1
@ TimMB Oh, ich habe deinen Beitrag falsch verstanden. Ich habe jemandes Anpassung gesehen, die das benutzt hat lastIndex(of:). Ich bin in diesem Fall völlig anderer Meinung über die Klarheit gegenüber dem Optimierungspunkt. Ich denke nicht, dass diese Implementierung besonders klar ist, insbesondere im Vergleich zu einer einfachen satzbasierten Lösung. In jedem Fall sollte dieser Code in eine Erweiterungsfunktion extrahiert werden. Dieser Algorithmus wird selbst bei einer geringen Eingabegröße wie Tausenden bis Zehntausenden grundsätzlich unbrauchbar. Es ist nicht schwer, solche Datensätze zu finden. Menschen können Tausende von Songs, Dateien, Kontakten usw. haben.
Alexander - Reinstate Monica

6

Für Arrays, in denen die Elemente weder Hashbar noch Vergleichbar sind (z. B. komplexe Objekte, Wörterbücher oder Strukturen), bietet diese Erweiterung eine allgemeine Möglichkeit, Duplikate zu entfernen:

extension Array
{
   func filterDuplicate<T:Hashable>(_ keyValue:(Element)->T) -> [Element]
   {
      var uniqueKeys = Set<T>()
      return filter{uniqueKeys.insert(keyValue($0)).inserted}
   }

   func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element]
   { 
      return filterDuplicate{"\(keyValue($0))"}
   }
}

// example usage: (for a unique combination of attributes):

peopleArray = peopleArray.filterDuplicate{ ($0.name, $0.age, $0.sex) }

or...

peopleArray = peopleArray.filterDuplicate{ "\(($0.name, $0.age, $0.sex))" }

Sie müssen sich nicht darum kümmern, Werte Hashable zu machen, und Sie können verschiedene Kombinationen von Feldern für die Eindeutigkeit verwenden.

Hinweis: Für einen robusteren Ansatz lesen Sie bitte die von Coeur vorgeschlagene Lösung in den Kommentaren unten.

stackoverflow.com/a/55684308/1033581

[EDIT] Swift 4 Alternative

Mit Swift 4.2 können Sie die Hasher-Klasse verwenden, um einen Hash viel einfacher zu erstellen. Die obige Erweiterung könnte geändert werden, um dies zu nutzen:

extension Array
{
    func filterDuplicate(_ keyValue:((AnyHashable...)->AnyHashable,Element)->AnyHashable) -> [Element]
    {
        func makeHash(_ params:AnyHashable ...) -> AnyHashable
        { 
           var hash = Hasher()
           params.forEach{ hash.combine($0) }
           return hash.finalize()
        }  
        var uniqueKeys = Set<AnyHashable>()
        return filter{uniqueKeys.insert(keyValue(makeHash,$0)).inserted}     
    }
}

Die aufrufende Syntax ist etwas anders, da der Abschluss einen zusätzlichen Parameter erhält, der eine Funktion zum Hashing einer variablen Anzahl von Werten enthält (die einzeln Hash-fähig sein müssen).

peopleArray = peopleArray.filterDuplicate{ $0($1.name, $1.age, $1.sex) } 

Es funktioniert auch mit einem einzelnen Eindeutigkeitswert (unter Verwendung von $ 1 und Ignorieren von $ 0).

peopleArray = peopleArray.filterDuplicate{ $1.name } 

Dies kann je nach Verhalten zu zufälligen Ergebnissen führen "\()", da Sie möglicherweise keine eindeutigen Werte erhalten, wie z Hashable. Beispiel: Wenn Ihre Elemente übereinstimmen, Printableindem alle dasselbe zurückgeben description, schlägt Ihre Filterung fehl.
Cœur

Einverstanden. Die Auswahl der Felder (oder Formeln), die das gewünschte Eindeutigkeitsmuster erzeugen, muss dies berücksichtigen. Für viele Anwendungsfälle bietet dies eine einfache Ad-hoc-Lösung, bei der die Klasse oder Struktur des Elements nicht geändert werden muss.
Alain T.

2
@AlainT. Tu das nicht wirklich. Der Zweck von String besteht nicht darin, ein Mechanismus zur Generierung von Ad-hoc-Schlüsseln im Ghetto zu sein. Beschränken Sie sich einfach Tauf das Sein Hashable.
Alexander - Reinstate Monica

@ Alexander Ich habe diese Idee in einer neuen Antwort angewendet
Cœur

Perfekte Antwort wie ich will. Ich danke dir sehr.
Hardik Thakkar

4

Sie können eine Set-Sammlung direkt verwenden, um Duplikate zu entfernen und sie dann wieder in ein Array umzuwandeln

var myArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
var mySet = Set<Int>(myArray)

myArray = Array(mySet) // [2, 4, 60, 6, 15, 24, 1]

Dann können Sie Ihr Array bestellen, wie Sie möchten

myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]

"Dann können Sie Ihr Array so bestellen, wie Sie möchten." Was ist, wenn ich die gleiche Reihenfolge wie beim ursprünglichen Array möchte? Es ist nicht so leicht.
Alexander - Reinstate Monica

3

Etwas prägnantere Syntaxversion von Daniel Kroms Swift 2-Antwort unter Verwendung eines abschließenden Verschlusses und eines Kurzargumentnamens, der anscheinend auf der ursprünglichen Antwort von Airspeed Velocity basiert :

func uniq<S: SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] {
  var seen = [E: Bool]()
  return source.filter { seen.updateValue(true, forKey: $0) == nil }
}

Beispiel für einen benutzerdefinierten Typ implementieren, die mit verwendet werden können uniq(_:)(die entsprechen müssen Hashable, und damit Equatable, weil Hashablesich Equatable):

func ==(lhs: SomeCustomType, rhs: SomeCustomType) -> Bool {
  return lhs.id == rhs.id // && lhs.someOtherEquatableProperty == rhs.someOtherEquatableProperty
}

struct SomeCustomType {

  let id: Int

  // ...

}

extension SomeCustomType: Hashable {

  var hashValue: Int {
    return id
  }

}

Im obigen Code ...

id, wie bei der Überladung von verwendet ==, kann ein beliebiger EquatableTyp sein (oder eine Methode, die einen EquatableTyp zurückgibt , z someMethodThatReturnsAnEquatableType(). B. ). Der auskommentierte Code demonstriert die Erweiterung der Prüfung auf Gleichheit, wobei someOtherEquatablePropertyes sich um eine andere Eigenschaft eines EquatableTyps handelt (es kann sich aber auch um eine Methode handeln, die einen EquatableTyp zurückgibt ).

id, wie in der hashValueberechneten Eigenschaft verwendet (erforderlich, um konform zu sein Hashable), kann eine beliebige Hashable(und damit Equatable) Eigenschaft (oder Methode, die einen HashableTyp zurückgibt ) sein.

Anwendungsbeispiel uniq(_:):

var someCustomTypes = [SomeCustomType(id: 1), SomeCustomType(id: 2), SomeCustomType(id: 3), SomeCustomType(id: 1)]

print(someCustomTypes.count) // 4

someCustomTypes = uniq(someCustomTypes)

print(someCustomTypes.count) // 3

Es ist nicht erforderlich, a zu verwenden Bool, wenn der einzige Wert, den Sie verwenden, ist true. Sie greifen nach einem "Einheitentyp" (einem Typ mit nur einem möglichen Wert). Swifts Einheitentyp ist Void, dessen einziger Wert ()(auch bekannt als leeres Tupel) ist. Sie können also einfach verwenden [T: Void]. Obwohl Sie das nicht tun sollten, weil Sie im Grunde nur erfunden haben Set. Verwenden Sie Setstattdessen. Siehe stackoverflow.com/a/55684308/3141234
Alexander - Reinstate Monica

3

Wenn Sie sortierte Werte benötigen, funktioniert dies (Swift 4)

let sortedValues = Array(Set(array)).sorted()


2
In diesem Fall verlieren Sie die Reihenfolge der Elemente.
Shmidt

Überhaupt nicht, dafür ist das .sorted()am Ende da. Grüße.
Mauricio Chirino

@ MauricioChirino Und wenn Ihr ursprüngliches Array war [2, 1, 1]? Es würde herauskommen [1, 2], das ist nicht bestellt: p
Alexander - Reinstate Monica

2
@ MauricioChirino Nein, bin ich nicht. Wenn das Ziel darin besteht, doppelte Werte aus einer Sequenz zu entfernen und dabei die Reihenfolge beizubehalten, in der Elemente eindeutig angezeigt wurden, funktioniert dies nicht. Das sehr klare Gegenbeispiel ist [2, 1, 1]. Das erste Auftreten von einzigartigen Elementen in der Reihenfolge ist [2, 1]. Das ist die richtige Antwort. Aber Ihren (falschen) Algorithmus verwendet, erhalten Sie [1, 2], das ist sortiert, aber ist nicht in dem richtigen, ursprünglich, um.
Alexander - Reinstate Monica

2
Schlägt fehl, wenn die Elemente in arraynicht vorhanden sind Hashable. HashableEinem Set können nur Datentypen hinzugefügt werden, einem Array kann jedoch jeder Datentyp hinzugefügt werden.
Mecki

3

Hier ist eine Lösung, die

  • Verwendet kein Vermächtnis NS Typen
  • Ist einigermaßen schnell mit O(n)
  • Ist prägnant
  • Erhält die Elementreihenfolge
extension Array where Element: Hashable {

    var uniqueValues: [Element] {
        var allowed = Set(self)
        return compactMap { allowed.remove($0) }
    }
}

2

Hier habe ich eine O (n) -Lösung für Objekte durchgeführt. Nicht wenige Zeilen Lösung, aber ...

struct DistinctWrapper <T>: Hashable {
    var underlyingObject: T
    var distinctAttribute: String
    var hashValue: Int {
        return distinctAttribute.hashValue
    }
}
func distinct<S : SequenceType, T where S.Generator.Element == T>(source: S,
                                                                distinctAttribute: (T) -> String,
                                                                resolution: (T, T) -> T) -> [T] {
    let wrappers: [DistinctWrapper<T>] = source.map({
        return DistinctWrapper(underlyingObject: $0, distinctAttribute: distinctAttribute($0))
    })
    var added = Set<DistinctWrapper<T>>()
    for wrapper in wrappers {
        if let indexOfExisting = added.indexOf(wrapper) {
            let old = added[indexOfExisting]
            let winner = resolution(old.underlyingObject, wrapper.underlyingObject)
            added.insert(DistinctWrapper(underlyingObject: winner, distinctAttribute: distinctAttribute(winner)))
        } else {
            added.insert(wrapper)
        }
    }
    return Array(added).map( { return $0.underlyingObject } )
}
func == <T>(lhs: DistinctWrapper<T>, rhs: DistinctWrapper<T>) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

// tests
// case : perhaps we want to get distinct addressbook list which may contain duplicated contacts like Irma and Irma Burgess with same phone numbers
// solution : definitely we want to exclude Irma and keep Irma Burgess
class Person {
    var name: String
    var phoneNumber: String
    init(_ name: String, _ phoneNumber: String) {
        self.name = name
        self.phoneNumber = phoneNumber
    }
}

let persons: [Person] = [Person("Irma Burgess", "11-22-33"), Person("Lester Davidson", "44-66-22"), Person("Irma", "11-22-33")]
let distinctPersons = distinct(persons,
    distinctAttribute: { (person: Person) -> String in
        return person.phoneNumber
    },
    resolution:
    { (p1, p2) -> Person in
        return p1.name.characters.count > p2.name.characters.count ? p1 : p2
    }
)
// distinctPersons contains ("Irma Burgess", "11-22-33") and ("Lester Davidson", "44-66-22")

1
Anstatt a Setmit einem Benutzerdefinierten zu verwenden DistinctWrapper, sollten Sie a Dictionaryvon differentAttributes für Objekte verwenden. Wenn Sie dieser Logik folgen, werden Sie schließlich [ Dictionary.init(_:uniquingKeysWith:)] pastebin.com/w90pVe0p(https://developer.apple.com/documentation/… implementieren , das jetzt in die Standardbibliothek integriert ist. Überprüfen Sie, wie einfach dies ist pastebin.com/w90pVe0p
Alexander - Reinstate Monica

2

Ich habe die Antwort von @ Jean-Philippe Pellet verwendet und eine Array-Erweiterung erstellt, die satzartige Operationen für Arrays ausführt, während die Reihenfolge der Elemente beibehalten wird.

/// Extensions for performing set-like operations on lists, maintaining order
extension Array where Element: Hashable {
  func unique() -> [Element] {
    var seen: [Element:Bool] = [:]
    return self.filter({ seen.updateValue(true, forKey: $0) == nil })
  }

  func subtract(takeAway: [Element]) -> [Element] {
    let set = Set(takeAway)
    return self.filter({ !set.contains($0) })
  }

  func intersect(with: [Element]) -> [Element] {
    let set = Set(with)
    return self.filter({ set.contains($0) })
  }
}

Es ist nicht erforderlich, a zu verwenden Bool, wenn der einzige Wert, den Sie verwenden, ist true. Sie greifen nach einem "Einheitentyp" (einem Typ mit nur einem möglichen Wert). Swifts Einheitentyp ist Void, dessen einziger Wert ()(auch bekannt als leeres Tupel) ist. Sie können also einfach verwenden [T: Void]. Obwohl Sie das nicht tun sollten, weil Sie im Grunde nur erfunden haben Set. Verwenden Sie Setstattdessen. Siehe stackoverflow.com/a/55684308/3141234
Alexander - Reinstate Monica

2

Dies ist nur eine sehr einfache und bequeme Implementierung. Eine berechnete Eigenschaft in einer Erweiterung eines Arrays mit gleichwertigen Elementen.

extension Array where Element: Equatable {
    /// Array containing only _unique_ elements.
    var unique: [Element] {
        var result: [Element] = []
        for element in self {
            if !result.contains(element) {
                result.append(element)
            }
        }

        return result
    }
}

1
Das ist auch so O(n^2).
Alexander - Reinstate Monica

2
func removeDublicate (ab: [Int]) -> [Int] {
var answer1:[Int] = []
for i in ab {
    if !answer1.contains(i) {
        answer1.append(i)
    }}
return answer1
}

Verwendung:

let f = removeDublicate(ab: [1,2,2])
print(f)

Ich denke, das ist das einfachste
Jack Rus

es hält die Reihenfolge und gibt Ihnen ein Array, das Sie wollen
Jack Rus

Das ist auch so O(n²).
Alexander - Reinstate Monica

2
  1. Fügen Sie zunächst alle Elemente eines Arrays zu NSOrderedSet hinzu.
  2. Dadurch werden alle Duplikate in Ihrem Array entfernt.
  3. Konvertieren Sie diese geordnete Menge erneut in ein Array.

Getan....

Beispiel

let array = [1,1,1,1,2,2,2,2,4,6,8]

let orderedSet : NSOrderedSet = NSOrderedSet(array: array)

let arrayWithoutDuplicates : NSArray = orderedSet.array as NSArray

Ausgabe von arrayWithoutDuplicates - [1,2,4,6,8]


2

Eine leicht verkürzte Version, die auf der Antwort der Array-Erweiterung von @ Jean-Philippe Pellet basiert:

extension Array where Element: Hashable {

    var uniques: Array {
        var added = Set<Element>()
        return filter { element in
            defer { added.insert(element) }
            return !added.contains(element)
        }
    }
}

Dies führt zwei Hashing-Operationen pro Element aus, was nicht erforderlich ist. insertGibt ein Tupel zurück, das angibt, ob das Element bereits vorhanden war oder zum ersten Mal hinzugefügt wurde. stackoverflow.com/a/55684308/3141234 Bitte löschen Sie diese Antwort.
Alexander - Monica

1

Sie können immer ein Wörterbuch verwenden, da ein Wörterbuch nur eindeutige Werte enthalten kann. Beispielsweise:

var arrayOfDates: NSArray = ["15/04/01","15/04/01","15/04/02","15/04/02","15/04/03","15/04/03","15/04/03"]

var datesOnlyDict = NSMutableDictionary()
var x = Int()

for (x=0;x<(arrayOfDates.count);x++) {
    let date = arrayOfDates[x] as String
    datesOnlyDict.setValue("foo", forKey: date)
}

let uniqueDatesArray: NSArray = datesOnlyDict.allKeys // uniqueDatesArray = ["15/04/01", "15/04/03", "15/04/02"]

println(uniqueDatesArray.count)  // = 3

Wie Sie sehen können, befindet sich das resultierende Array nicht immer in der richtigen Reihenfolge. Wenn Sie das Array sortieren / bestellen möchten, fügen Sie Folgendes hinzu:

var sortedArray = sorted(datesOnlyArray) {
(obj1, obj2) in

    let p1 = obj1 as String
    let p2 = obj2 as String
    return p1 < p2
}

println(sortedArray) // = ["15/04/01", "15/04/02", "15/04/03"]

.


1

Am einfachsten wäre es, NSOrderedSet zu verwenden, das eindeutige Elemente speichert und die Reihenfolge der Elemente beibehält. Mögen:

func removeDuplicates(from items: [Int]) -> [Int] {
    let uniqueItems = NSOrderedSet(array: items)
    return (uniqueItems.array as? [Int]) ?? []
}

let arr = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
removeDuplicates(from: arr)

Ich frage mich, wie diese Leistung mit den besseren Antworten hier verglichen wird. Hast du verglichen?
Alexander - Reinstate Monica
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.