Usuwanie duplikatów elementów z tablicy w Swift

252

W Swift po prostu piszesz, Set( yourArray )aby tablica była unikalna. (W razie potrzeby zamówiony zestaw).

Zanim było to możliwe, jak to się stało?


Mogę mieć tablicę, która wygląda następująco:

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

Lub, naprawdę, dowolna sekwencja podobnie wpisanych części danych. Chcę tylko upewnić się, że istnieje tylko jeden identyczny element. Na przykład powyższa tablica wyglądałaby następująco:

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

Zauważ, że duplikaty 2, 6 i 15 zostały usunięte, aby upewnić się, że istnieje tylko jeden z każdego identycznego elementu. Czy Swift zapewnia sposób na to łatwo, czy też muszę to zrobić sam?

Altair357
źródło
11
Najprostszym sposobem jest konwersja tablicy w NSSet, NSSet to nieuporządkowana kolekcja obiektów, jeśli trzeba zachować porządek NSOrdersSet.
Andrea
Możesz użyć funkcji przecięcia, jak można znaleźć w tej klasie z funkcjami tablic: github.com/pNre/ExSwift/blob/master/ExSwift/Array.swift
Edwin Vermeer
Nie jest częścią Swift, ale używam Dollara. $.uniq(array) github.com/ankurp/Dollar#uniq---uniq
Andy
Prawdopodobnie najbardziej elegancką, najmądrzejszą i najszybszą odpowiedź zapewnia odpowiedź mxcl poniżej. Co również pomaga utrzymać porządek
Honey
1
Dlaczego nie korzystasz Setz Swift? Będziesz mógł podać listę nieuporządkowanych i unikalnych elementów.
TibiaZ,

Odpowiedzi:

133

Możesz rzucić własny, np. Tak ( zaktualizowany do Swift 1.2 z zestawem ):

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]

Wersja Swift 3:

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
}

I jako rozszerzenie dla 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
    }
}
Jean-Philippe Pellet
źródło
12
Możesz również zaimplementować treść tej funkcji jakovar addedDict = [T:Bool](); return filter(source) { addedDict(true, forKey: $0) == nil }
Airspeed Velocity
1
@AirspeedVelocity: Czy chodziło Ci updateValue(true, forKey: $0)...oaddedDict(true, forKey: $0)...
Jawwad,
1
Ups tak przepraszam przypadkowo metoda! Powinno być return filter(source) { addedDict.updateValue(true, forKey: $0) == nil }tak, jak mówisz.
Prędkość
21
Tylko słowo ostrzeżenia: Unikaj omawiania wydajności dla takich prostych funkcji, dopóki nie będziesz w stanie polegać na ich wydajności, w którym to momencie jedyną rzeczą, którą powinieneś zrobić, jest test porównawczy. Zbyt często widziałem niemożliwy do utrzymania kod lub nawet kod mniej wydajny z powodu poczynienia założeń. :) Prawdopodobnie łatwiej to zrozumieć:let uniques = Array(Set(vals))
Blixt
11
@Blixt Zgoda. Po raz kolejny zaleta polega na uszanowaniu kolejności elementów oryginalnej tablicy.
Jean-Philippe Pellet
493

Możesz dość łatwo przekonwertować na zestaw i ponownie z powrotem na tablicę:

let unique = Array(Set(originals))

Nie gwarantuje się utrzymania oryginalnej kolejności tablicy.

Ben Packard
źródło
37
Czy istnieje sposób użycia zestawu przy zachowaniu oryginalnej kolejności tablicy?
Crashalot
6
@ Crashalot Zobacz moją odpowiedź.
Jean-Philippe Pellet
5
Jeśli chcesz zachować unikatowość obiektów za pomocą określonej właściwości, zaimplementuj także protokół Hashable i Equatable dla tej klasy, zamiast po prostu użyć transformacji Array-> Set-> Array
Fawkes
2
Miły!! Jaka jest złożoność czasowa tego rozwiązania?
JW.ZG
2
Nie działa, jeśli elementy w nim originalsnie są Hashable; Hashabledo zestawu można dodawać tylko typy danych, ale do tablicy można dodawać dowolny typ danych.
Mecki,
69

Wiele odpowiedzi jest dostępnych tutaj, ale brakowało mi tego prostego rozszerzenia, odpowiedniego dla Swift 2 i nowszych:

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

Sprawia, że ​​jest to bardzo proste. Można nazwać tak:

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

Filtrowanie na podstawie właściwości

Aby przefiltrować tablicę na podstawie właściwości, możesz użyć tej metody:

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

Możesz zadzwonić w następujący sposób:

let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }
Antoine
źródło
@Antoine Dziękujemy za filtrowanie na podstawie rozszerzenia właściwości. To jest naprawdę przydatne. Ale czy możesz wyjaśnić, jak to działa. To dla mnie zbyt trudne do zrozumienia. Dziękuję
Mostafa Mohamed Raafat
Aktualizacje dla swift 3: func filterDuplicates (_ includeElement: (_ lhs: Element, _ rhs: Element) -> Bool) -> [Element] {
cartart
Pierwsza część tej odpowiedzi ( extension Array where Element: Equatable) jest zastępowana przez stackoverflow.com/a/36048862/1033581, który oferuje bardziej wydajne rozwiązanie ( extension Sequence where Iterator.Element: Equatable).
Cœur
7
Będzie to miało O(n²)wydajność czasową, co jest naprawdę złe dla dużych tablic.
Duncan C
Powinieneś użyć zestawu do śledzenia elementów widocznych do tej pory, aby sprowadzić tę straszliwą O(n²)złożoność z powrotem doO(n)
Alexander - Przywróć Monikę
63

Swift 3.0

let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))
Jovan Stankovic
źródło
1
niech unikatowyZamówienie = Tablica (NSOrdersSet (tablica: nazwy_użytkownika)) jak! [String] jeśli masz tablicę String, a nie Any
Zaporozhchenko Oleksandr
Nie działa, jeśli elementy w nim arraynie są Hashable; Hashabledo zestawu można dodawać tylko typy danych, ale do tablicy można dodawać dowolny typ danych.
Mecki
Testowany w Swift 5.1b5, biorąc pod uwagę, że elementy są haszowalne i chęć zachowania porządku, NSOrdersSet (array: array) .array jest nieznacznie szybszy niż czysta funkcja swift uniqued () przy użyciu zestawu z filtrem. Testowałem z 5100 ciągami, które dały 13 unikalnych wartości.
dlemex,
62

Jeśli umieścisz oba rozszerzenia w kodzie, szybsza Hashablewersja zostanie użyta, gdy to możliwe, a Equatablewersja zostanie wykorzystana jako rezerwowa.

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

Jeśli kolejność nie jest ważna, zawsze możesz użyć tego inicjatora Ustaw .

Jessy
źródło
okej, rozumiem nie mogłem tego nazwać, ponieważ moja tablica jest tablicą struktur ... jak miałbym to zrobić w moim przypadku? struct 20 różnych zmiennych, string i [string]
David Seek
@David Seek Wygląda na to, że nie uczyniłeś swojego ścisłego haszowania ani równego. Czy to jest poprawne?
Jessy,
1
@DavidSeek like this, uniqueArray = nonUniqueArray.uniqueElements
Mert Celik
tak, nie martw się. zaraz po tym. już prawie 2 lata: P
David Seek
Będzie to miało O(n²)wydajność czasową, co jest naprawdę złe dla dużych tablic.
Duncan C
44

edytuj / aktualizuj Swift 4 lub nowszy

Możemy również rozszerzyć RangeReplaceableCollectionprotokół, aby umożliwić jego stosowanie również z StringProtocoltypami:

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"

Metoda mutacji:

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

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

Aby wyświetlić Swift 3, kliknij tutaj

Leo Dabus
źródło
1
Podoba mi się to, działa również z wieloma słownikami!
DeyaEldeen
6
O (N ^ 2) jest złe :(
Alexander - Przywróć Monikę
1
@Alexander Leo Dabus zastąpił reduceimplementację, więc teraz złożoność jest inna.
Cœur
1
Wyniki są interesujące. Zarówno dla 1 miliona unikalnych przedmiotów, jak i 8 milionów wersja filtru jest szybsza. Jednak wersja oparta na filtrze zajmuje 8,38x dłużej dla 8 milionów unikalnych elementów (włos w O(n)czasie), podczas gdy wersja oparta na płaskiej mapie zajmuje 7,47x dłużej dla 8 milionów unikalnych wpisów niż 1 milion, co sugeruje, że wersja oparta na płaskiej mapie skaluje się lepiej . W jakiś sposób wersja oparta na płaskiej mapie działa nieco lepiej niż O(n)czas!
Duncan C
1
W rzeczywistości, kiedy uruchamiam test z 64x większą liczbą elementów w tablicy, wersja oparta na płaskiej mapie jest szybsza.
Duncan C
43

Szybki 4

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

każda próba insertbędzie również powrót krotki: (inserted: Bool, memberAfterInsert: Set.Element). Zobacz dokumentację .

Korzystanie ze zwróconej wartości pomaga nam uniknąć zapętlania lub wykonywania innych operacji.

mxcl
źródło
7
Po prostym profilowaniu ta metoda jest naprawdę szybka. Jest setki razy szybszy niż użycie zmniejszania (_: _ :), a nawet zmniejszania (do: _ :)
Kelvin
3
@ Kelvin Ponieważ wszystkie te algorytmy były O(n^2)i nikt tego nie zauważył.
Alexander - Przywróć Monikę
@Kelvin ta odpowiedź jest identyczna jak odpowiedź Eneko Alonso + mój komentarz (16 czerwca 17).
Cœur
27

Szybki 4

Gwarantujemy utrzymanie zamówienia.

extension Array where Element: Equatable {
    func removingDuplicates() -> Array {
        return reduce(into: []) { result, element in
            if !result.contains(element) {
                result.append(element)
            }
        }
    }
}
Alessandro Martin
źródło
Używam tego teraz, zmieniłem tylko nazwę metody, aby usunąć duplikaty :)
J. Doe
Myślę, że to rozwiązanie jest zwarta, ale wierzę, że rozwiązanie deanWombourne pisał przed rokiem może być nieco bardziej wydajne niż reduce: ogólnie rzecz biorąc, jest to po prostu jeszcze jedna linia w całym projekcie, aby napisać swoją funkcję jako: var unique: [Iterator.Element] = []; for element in self where !unique.contains(element) { unique.append(element) }; return unique. Przyznaję, że nie testowałem jeszcze względnych wyników.
Cœur
3
Będzie to miało O(n²)wydajność czasową, co jest naprawdę złe dla dużych tablic.
Duncan C
@NickGaens Nie, nie jest, to jest O(n²). Nie ma w tym nic szybkiego.
Alexander - Przywróć Monikę
@ Cœur reducelub reduce(into:)nie zrobiłby krytycznej różnicy. Przepisanie tego, aby nie powtarzać połączeń, containszrobiłoby DUŻO większą różnicę.
Alexander - Przywróć Monikę
16

Oto kategoria, w SequenceTypektórej zachowana jest pierwotna kolejność tablicy, ale używa ona Setdo wykonania containsodnośników, aby uniknąć O(n)kosztów contains(_:)metody Array .

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

Jeśli nie jesteś Hashable lub Equatable, możesz przekazać predykat w celu sprawdzenia równości:

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

Teraz, jeśli nie masz Hashable, ale jesteś Equatable, możesz użyć tej metody:

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: ==)
    }
}

Na koniec możesz dodać wersję klucza unikatowego w następujący sposób:

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] }
    }
}

Możesz umieścić oba te elementy w swojej aplikacji, Swift wybierze właściwą w zależności od Iterator.Elementtypu sekwencji .

dziekanWombourne
źródło
Hej, w końcu ktoś z O(n)rozwiązaniem. Nawiasem mówiąc, możesz połączyć operacje zestawów „zaznacz” i „wstaw” w jedną. Zobacz stackoverflow.com/a/46354989/3141234
Alexander - Przywróć Monikę
Och, to sprytne :)
dziekan Wombourne
14

Zainspirowany https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift , możemy zadeklarować mocniejsze narzędzie, które jest w stanie filtrować jedność na dowolnym keyPath. Dzięki komentarzom Aleksandra na różne odpowiedzi dotyczące złożoności poniższe rozwiązania powinny być prawie optymalne.

Rozwiązanie niemutujące

Rozszerzamy o funkcję, która jest w stanie filtrować unikalność na dowolnym keyPath:

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

Uwaga: w przypadku, gdy Twój obiekt nie jest zgodny z RangeReplaceableCollection, ale jest zgodny z Sekwencją, możesz mieć to dodatkowe rozszerzenie, ale typem zwracanym zawsze będzie 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 }
    }
}

Stosowanie

Jeśli chcemy jednoznaczności dla samych elementów, jak w pytaniu, używamy 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] */

Jeśli chcemy unicity dla czegoś innego (jak dla idzbioru obiektów), wówczas używamy wybranej ścieżki keyPath:

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}] */

Rozwiązanie mutujące

Rozszerzamy o funkcję mutacji, która może filtrować unikalność na dowolnym keyPath:

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

Stosowanie

Jeśli chcemy jednoznaczności dla samych elementów, jak w pytaniu, używamy 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] */

Jeśli chcemy unicity dla czegoś innego (jak dla idzbioru obiektów), wówczas używamy wybranej ścieżki keyPath:

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}] */
Cœur
źródło
1
To dobra implementacja! I tylko z tym, że ścieżki klucza były konwertowalne na zamknięcia, dzięki czemu można użyć argumentu zamknięcia do obsługi zarówno dowolnego kodu (w zamknięciach), jak i samych wyszukiwań właściwości (za pomocą ścieżek kluczowych). Jedyną zmianą, którą bym wprowadził, jest keyPathustawienie domyślne \.self, ponieważ prawdopodobnie jest to większość przypadków użycia.
Alexander - Przywróć Monikę
1
@Alexander Próbowałem przywrócić domyślną wartość do Self, ale wtedy musiałbym Elementzawsze robić Hashable. Alternatywą dla wartości domyślnej jest dodanie zwykłego przeciążenia bez parametrów:extension Sequence where Element: Hashable { func unique() { ... } }
Cœur
Ach tak, ma sens!
Alexander - Przywróć Monikę
1
Genialny ... prosty, a co najważniejsze „elastyczny”. Dzięki.
BonanzaDriver
12

Alternatywne (jeśli nie optymalne) rozwiązanie stąd wykorzystujące niezmienne typy zamiast zmiennych:

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
}

Uwzględniono w celu kontrastowania imperatywnego podejścia Jean-Pillippe z funkcjonalnym podejściem.

Jako bonus, ta funkcja działa zarówno z łańcuchami, jak i tablicami!

Edycja: Ta odpowiedź została napisana w 2014 roku dla Swift 1.0 (wcześniej Setbyła dostępna w Swift). Nie wymaga zgodności hashable i działa w kwadratowym czasie.

Pliskin
źródło
8
Uwaga, nie ma jednego, ale dwa sposoby, aby działało to w czasie kwadratowym - zarówno, jak containsi tablica, dołączają w O (n). Chociaż ma tę zaletę, że wymaga tylko możliwości zrównania, a nie mieszania.
Prędkość lotu
to jest naprawdę skomplikowany sposób pisania filter. To O (n ^ 2) (które jest wymagane, jeśli nie chcesz wymagać Hashablezgodności), ale powinieneś przynajmniej to wyraźnie nazwać
Alexander - Przywróć Monikę
10

szybki 2

z odpowiedzią funkcji uniq :

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

posługiwać się:

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
Daniel Krom
źródło
Ta Boolwartość jest oczywiście zbędna, ponieważ Twój kod nigdy jej nie czyta. Użyj Setzamiast a, Dictionarya dostaniesz moją opinię.
Nikolai Ruhe
10

W Swift 5

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

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

Wyjście będzie

 ["Sumit", "Mohan", "Amit", "Aman"]
Sanjay Mishra
źródło
2
Jest to powtórzenie wielu odpowiedzi już tutaj i nie zachowuje kolejności.
Alexander - Przywróć Monikę
9

Jeszcze jedno rozwiązanie Swift 3.0 do usuwania duplikatów z tablicy. To rozwiązanie poprawia wiele innych rozwiązań już zaproponowanych przez:

  • Zachowanie kolejności elementów w tablicy wejściowej
  • Złożoność liniowa O (n): filtr jednoprzebiegowy O (n) + wstawienie zestawu O (1)

Biorąc pod uwagę tablicę liczb całkowitych:

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]

Kod funkcjonalny:

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]

Kod rozszerzenia tablicy:

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]

Ten kod wykorzystuje wynik zwracany przez insertoperację on Set, która jest wykonywana O(1), i zwraca krotkę wskazującą, czy element został wstawiony lub czy już istniał w zestawie.

Jeśli element był w zestawie, filterwykluczy go z końcowego wyniku.

Eneko Alonso
źródło
1
Nie być wybrednym, ale będziesz przeprowadzać test wkładki i członkostwa tyle razy, ile jest elementów, więc powinieneś liczyć ich koszt również jako O (n). Nie oznacza to jednak 3xO (n), ponieważ te O i nie mają równego kosztu z filtrem, więc dodanie O (n) to jabłka do pomarańczy. Jeśli weźmiemy pod uwagę operacje ustawiania jako część O (1) kosztu filtra, złożoność to tylko O ​​(n), choć z większym „O”. Przesuwając to do granicy, można również uniknąć wstawiania, gdy element jest już w zestawie.
Alain T.
Masz rację, użycie deferkodu wykona operację testowania zestawu dwa razy, jeden z containsi jeden z insert. Podczas dalszej lektury dokumentacji Swift odkryłem, że insertzwraca krotkę wskazującą, czy element został wstawiony, czy nie, więc uprościłem kod usuwając containskontrolę.
Eneko Alonso
2
Miły. Twoje rozszerzenie może być optymalne, wykonując toextension Sequence where Iterator.Element: Hashable { ... }
Cœur
@AlainT. Nie. Oba inserti containsmają O(1)złożoność.O(1) + O(1) = O(1). Te dwie operacje są następnie wykonywane nrazy (jeden raz na wywołanie zamknięcia filter, które jest wywoływane raz na element) To znaczy, jeśli operacja zajmuje stałą ilość czasu niezależnie od wielkości wejściowej, to wykonanie tej czynności dwa razy nadal wymaga stałego czasu to jest niezależnie od rozmiaru wejściowego. Całkowita złożoność tego jest O(n).
Alexander - Przywróć Monikę
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] }
  }
}

stosowanie:

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

lub

["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
Rok Gregorič
źródło
Jest O(n^2). Nie rób tego
Alexander - Przywróć Monikę
8

Szybki 5

extension Sequence where Element: Hashable {
    func unique() -> [Element] {
        NSOrderedSet(array: self as! [Any]).array as! [Element]
    }
}
blackjacx
źródło
Zrobiłem trochę zmian, więc mogłem wybrać klucz do porównania. 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
Nie ma potrzeby używania Bool, gdy jedyną wartością, której używasz, jest true. Sięgasz po „typ jednostki” (typ z tylko jedną możliwą wartością). Typ jednostki Swifta to Void, którego jedyną wartością jest ()(inaczej pusta krotka). Więc możesz po prostu użyć [T: Void]. Chociaż nie powinieneś tego robić, ponieważ po prostu wynalazłeś Set. Użyj Setzamiast tego. Zobacz stackoverflow.com/a/55684308/3141234 Usuń tę odpowiedź.
Alexander - Przywróć Monikę
8

Myśl jak funkcjonalny programista :)

Aby przefiltrować listę na podstawie tego, czy element już wystąpił, potrzebujesz indeksu. Możesz użyć, enumeratedaby uzyskać indeks i mappowrócić do listy wartości.

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

To gwarantuje porządek. Jeśli nie masz nic przeciwko zamówieniu, to istniejąca odpowiedź Array(Set(myArray))jest prostsza i prawdopodobnie bardziej wydajna.


AKTUALIZACJA: Kilka uwag na temat wydajności i poprawności

Kilka osób skomentowało wydajność. Zdecydowanie jestem w szkole, pisząc najpierw poprawny i prosty kod, a potem odkrywam wąskie gardła, choć doceniam, że dyskusyjne jest to, czy jest to bardziej zrozumiałe niż Array(Set(array)).

Ta metoda jest znacznie wolniejsza niż Array(Set(array)). Jak zauważono w komentarzach, zachowuje porządek i działa na elementach, które nie są haszowalne.

Jednak metoda @Alain T zachowuje również porządek i jest znacznie szybsza. Tak więc, chyba że twój typ elementu nie jest haszowalny lub potrzebujesz tylko jednej linijki, to sugeruję skorzystanie z ich rozwiązania.

Oto kilka testów na MacBooku Pro (2014) na Xcode 11.3.1 (Swift 5.1) w trybie Release.

Funkcja profilera i dwie metody porównywania:

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

I niewielka różnorodność wejść testowych:

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

Podaje jako wynik:

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
Tim MB
źródło
1
w odróżnieniu od Array(Set(myArray))tego działa to w przypadku rzeczy, które nie sąHashable
Porter Child
1
... i w przeciwieństwie Array(Set(myArray))do kolejności w tablicy jest zachowana.
Sander Saelmans
To wydaje mi się najlepszą odpowiedzią, przynajmniej w chwili obecnej, gdy Swift 5 jest już aktualną wersją.
oradyvan
To bardzo eleganckie rozwiązanie; niestety jest również dość powolny.
Colin Stark
1
@ Timim Oh, źle odczytałem twój post. Widziałem czyjąś adaptację, która tego używała lastIndex(of:). W tym przypadku całkowicie nie zgadzam się co do jasności względem punktu optymalizacji. Nie sądzę, aby ta implementacja była szczególnie jasna, szczególnie w porównaniu z prostym rozwiązaniem opartym na zestawie. W każdym razie taki kod należy wyodrębnić do funkcji rozszerzenia. Algorytm ten staje się praktycznie bezużyteczny nawet przy niskim rozmiarze wejściowym, jak w tysiącach do dziesiątek tysięcy. Nie jest trudno znaleźć takie zestawy danych, ludzie mogą mieć tysiące piosenek, plików, kontaktów itp.
Alexander - Przywróć Monikę
6

W przypadku tablic, w których elementy nie są mieszalne ani porównywalne (np. Złożone obiekty, słowniki lub struktury), to rozszerzenie zapewnia ogólny sposób usuwania duplikatów:

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

Nie musisz zawracać sobie głowy tworzeniem wartości Hashable i pozwala to na użycie różnych kombinacji pól dla wyjątkowości.

Uwaga: aby uzyskać bardziej niezawodne podejście, zobacz rozwiązanie zaproponowane przez Coeur w komentarzach poniżej.

stackoverflow.com/a/55684308/1033581

[EDYCJA] Swift 4 alternatywa

W Swift 4.2 możesz użyć klasy Hasher, aby zbudować skrót znacznie łatwiej. Powyższe rozszerzenie można zmienić, aby to wykorzystać:

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

Składnia wywoływania jest nieco inna, ponieważ zamknięcie otrzymuje dodatkowy parametr zawierający funkcję mieszającą zmienną liczbę wartości (która musi być mieszalna indywidualnie)

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

Będzie również działał z jedną wartością unikalności (użycie 1 $ i ignorowanie 0 $).

peopleArray = peopleArray.filterDuplicate{ $1.name } 
Alain T.
źródło
Może to dawać losowe wyniki w zależności od zachowania "\()", ponieważ może nie dać unikalnych wartości, takich jak zgodność z Hashablepowinnością. Na przykład, jeśli wszystkie elementy są zgodne Printableprzez wszystkie zwracane to samo description, oznacza to, że filtrowanie nie powiedzie się.
Cœur
Zgoda. Wybór pól (lub formuł), które wytworzą pożądany wzór niepowtarzalności, będzie musiał wziąć to pod uwagę. W wielu przypadkach zapewnia to proste rozwiązanie ad-hoc, które nie wymaga zmiany klasy lub struktury elementu.
Alain T.
2
@AlainT. Nie rób tego, naprawdę. Celem String nie jest mechanizm generowania klucza ad-hoc w getcie. Ogranicz się Tdo bycia Hashable.
Alexander - Przywróć Monikę
@Alexander Zastosowałem ten pomysł w nowej odpowiedzi: stackoverflow.com/a/55684308/1033581
Środa
Idealna odpowiedź, jak chcę. Dziękuję bardzo.
Hardik Thakkar,
4

Możesz użyć bezpośrednio zestawu kolekcji, aby usunąć duplikat, a następnie rzutować go z powrotem do tablicy

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]

Następnie możesz zamówić swoją tablicę, jak chcesz

myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]
Vincent Choubard
źródło
„Zatem możesz zamówić swoją tablicę, jak chcesz” Co zrobić, jeśli chcę mieć taką samą kolejność jak w oryginalnej tablicy? To nie jest takie proste.
Alexander - Przywróć Monikę
3

Nieco bardziej zwięzła wersja składniowa odpowiedzi Swift 2 Daniela Kroma , wykorzystująca końcowe zamknięcie i krótką nazwę argumentu, która wydaje się być oparta na oryginalnej odpowiedzi Airspeed Velocity :

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

Przykład implementacji niestandardowego typu, którego można używać uniq(_:)(który musi być zgodny Hashable, a zatem Equatableponieważ Hashablerozszerza 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
  }

}

W powyższym kodzie ...

id, użyte w przeciążeniu ==, może być dowolny Equatabletyp (lub metoda, która zwraca Equatabletyp, np someMethodThatReturnsAnEquatableType().). Skomentowany kod pokazuje rozszerzenie sprawdzania równości, gdzie someOtherEquatablePropertyjest inną właściwością Equatabletypu (ale może być również metodą zwracającą Equatabletyp).

id, użyte we hashValuewłaściwości obliczonej (wymaganej do zgodności Hashable), może być dowolną Hashable(a tym samym Equatable) właściwością (lub metodą zwracającą Hashabletyp).

Przykład użycia 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
Scott Gardner
źródło
Nie ma potrzeby używania Bool, gdy jedyną wartością, której używasz, jest true. Sięgasz po „typ jednostki” (typ z tylko jedną możliwą wartością). Typ jednostki Swifta to Void, którego jedyną wartością jest ()(inaczej pusta krotka). Więc możesz po prostu użyć [T: Void]. Chociaż nie powinieneś tego robić, ponieważ po prostu wynalazłeś Set. Użyj Setzamiast tego. Zobacz stackoverflow.com/a/55684308/3141234
Alexander - Przywróć Monikę
3

Jeśli potrzebujesz posortowanych wartości, działa to (Swift 4)

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

Mauricio Chirino
źródło
2
W tym przypadku tracisz kolejność elementów.
Shmidt
Wcale nie, po to .sorted()jest koniec. Pozdrowienia.
Mauricio Chirino,
@MauricioChirino A jeśli Twoja oryginalna tablica była [2, 1, 1]? Wyjdzie [1, 2], to nie jest zamówione: p
Alexander - Przywróć Monikę
2
@MauricioChirino Nie, nie jestem. Jeśli celem jest usunięcie zduplikowanych wartości z sekwencji przy jednoczesnym zachowaniu kolejności, w której elementy pojawiały się wyjątkowo, nie robi tego. Bardzo wyraźnym przykładem jest[2, 1, 1]. Pierwsze pojawienie się unikalnych elementów, w kolejności, to[2, 1]. To poprawna odpowiedź. Ale używając (niepoprawnego) algorytmu otrzymujesz[1, 2], który jest posortowany, ale nie ma prawidłowej, oryginalnej kolejności.
Alexander - Przywróć Monikę
2
Nie działa, jeśli elementy w nim arraynie są Hashable; Hashabledo zestawu można dodawać tylko typy danych, ale do tablicy można dodawać dowolny typ danych.
Mecki,
3

Oto rozwiązanie, które

  • Nie używa żadnych starszych NStypów
  • Jest dość szybki z O(n)
  • Jest zwięzły
  • Zachowuje porządek elementu
extension Array where Element: Hashable {

    var uniqueValues: [Element] {
        var allowed = Set(self)
        return compactMap { allowed.remove($0) }
    }
}
Erik Aigner
źródło
2

tutaj zrobiłem jakieś O (n) rozwiązanie dla obiektów. Nie kilka linii, ale ...

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")
kas-kad
źródło
1
Zamiast używać Setz niestandardowym DistinctWrapper, powinieneś użyć Dictionaryz odrębnych atrybutów do obiektów. Kiedy będziesz postępować zgodnie z tą logiką, ostatecznie skończysz implementować [ Dictionary.init(_:uniquingKeysWith:)] pastebin.com/w90pVe0p ( https://developer.apple.com/documentation/... , który jest teraz wbudowany w bibliotekę standardową. Sprawdź, jak proste jest to pastebin.com/w90pVe0p
Alexander - Przywróć Monikę
2

Użyłem odpowiedzi @ Jean-Philippe Pelleta i stworzyłem rozszerzenie Array, które wykonuje operacje podobne do zestawów na tablicach, zachowując kolejność elementów.

/// 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) })
  }
}
Will Richardson
źródło
Nie ma potrzeby używania Bool, gdy jedyną wartością, której używasz, jest true. Sięgasz po „typ jednostki” (typ z tylko jedną możliwą wartością). Typ jednostki Swifta to Void, którego jedyną wartością jest ()(inaczej pusta krotka). Więc możesz po prostu użyć [T: Void]. Chociaż nie powinieneś tego robić, ponieważ po prostu wynalazłeś Set. Użyj Setzamiast tego. Zobacz stackoverflow.com/a/55684308/3141234
Alexander - Przywróć Monikę
2

To tylko bardzo prosta i wygodna implementacja. Obliczona właściwość w rozszerzeniu tablicy, która ma elementy możliwe do porównania.

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
    }
}
DaveAMoore
źródło
1
To jest również O(n^2).
Alexander - Przywróć Monikę
2
func removeDublicate (ab: [Int]) -> [Int] {
var answer1:[Int] = []
for i in ab {
    if !answer1.contains(i) {
        answer1.append(i)
    }}
return answer1
}

Stosowanie:

let f = removeDublicate(ab: [1,2,2])
print(f)
Jack Rus
źródło
Myślę, że to najprostsze
Jack Rus
zachowuje porządek i daje tablicę, którą chcesz
Jack Rus
To jest również O(n²).
Alexander - Przywróć Monikę
2
  1. Najpierw dodaj wszystkie elementy tablicy do NSOrdersSet.
  2. Spowoduje to usunięcie wszystkich duplikatów z tablicy.
  3. Ponownie przekonwertuj ten zestaw uporządkowany na tablicę.

Gotowe....

Przykład

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

wyjście arrayWithoutDuplicates - [1,2,4,6,8]

Mahendra Thotakura
źródło
2

Lekko zwarta wersja oparta na rozszerzeniu tablicy @ Jean-Philippe Pelleta:

extension Array where Element: Hashable {

    var uniques: Array {
        var added = Set<Element>()
        return filter { element in
            defer { added.insert(element) }
            return !added.contains(element)
        }
    }
}
Sander Saelmans
źródło
Wykonuje dwie operacje skrótu na element, co jest niepotrzebne. insertzwraca krotkę, która mówi, czy element już tam był, czy został dodany po raz pierwszy. stackoverflow.com/a/55684308/3141234 Usuń tę odpowiedź.
Alexander - Przywróć Monikę
1

Zawsze możesz użyć Słownika, ponieważ Słownik może przechowywać tylko unikalne wartości. Na przykład:

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

Jak widać, wynikowa tablica nie zawsze będzie „uporządkowana”. Jeśli chcesz posortować / zamówić tablicę, dodaj to:

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"]

.

AT3D
źródło
1

Najprostszym sposobem byłoby użycie NSOrdersSet, który przechowuje unikatowe elementy i zachowuje kolejność elementów. Lubić:

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)
sgl0v
źródło
Zastanawiam się, jak ten występ wypada w porównaniu z lepszymi odpowiedziami tutaj. Porównywałeś?
Alexander - Przywróć Monikę