Удаление повторяющихся элементов из массива в Swift

У меня может быть массив, который выглядит следующим образом:

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

Или, на самом деле, любая последовательность однотипных порций данных. Я хочу убедиться, что каждый идентичный элемент присутствует только по одному. Например, приведенный выше массив будет выглядеть следующим образом:

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

Обратите внимание, что дубликаты 2, 6 и 15 были удалены, чтобы гарантировать, что каждый идентичный элемент был только по одному. Предоставляет ли Swift способ сделать это легко, или мне придется делать это самому?


person Altair357    schedule 09.09.2014    source источник
comment
Самый простой способ - преобразовать массив в NSSet, NSSet представляет собой неупорядоченный набор объектов, если необходимо сохранить порядок NSOrderedSet.   -  person Andrea    schedule 09.09.2014
comment
Вы можете использовать функцию пересечения, как вы можете найти в этом классе с функциями для массивов: github.com/pNre/ExSwift/blob/master/ExSwift/Array.swift   -  person Edwin Vermeer    schedule 09.09.2014
comment
Не является частью Swift, но я использую Dollar. $.uniq(array) github.com/ankurp/Dollar#uniq---uniq   -  person Andrew    schedule 06.12.2016
comment
Вероятно, самый элегантный, умный и быстрый ответ дает ответ mxcl ниже. Что также помогает поддерживать порядок   -  person Honey    schedule 10.05.2018
comment
Почему бы вам просто не использовать Set от Swift? Вы сможете предоставить список неупорядоченных и уникальных элементов.   -  person TibiaZ    schedule 11.03.2019
comment
Я согласен с @Andrea. Вам нужно использовать NSOrderedSet, если вы хотите удалить повторяющиеся элементы за время O (N) и сохранить порядок. NSOrderedSet содержит набор и массив внутри него: набор предназначен для проверки дублирования, а массив - для поддержания порядка. Если вы не хотите использовать NSOrderedSet, вы можете просто использовать другой Set для проверки дублирования. Надеюсь, это поможет.   -  person HongchaoZhang    schedule 14.08.2019


Ответы (45)


Вы можете свернуть свой собственный, например нравится:

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

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

И как расширение для Array:

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

Или более элегантно (Swift 4/5):

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

Что будет использоваться:

[1,2,4,2,1].uniqued()  // => [1,2,4]
person Jean-Philippe Pellet    schedule 09.09.2014
comment
Вы также можете реализовать тело этой функции как var addedDict = [T:Bool](); return filter(source) { addedDict(true, forKey: $0) == nil } - person Airspeed Velocity; 23.12.2014
comment
Очень хорошо. Отныне ваше решение будет моим предпочтительным подходом к такого рода ситуациям. - person Jean-Philippe Pellet; 24.12.2014
comment
@AirspeedVelocity: Вы имели в виду updateValue(true, forKey: $0)... вместо addedDict(true, forKey: $0)... - person Jawwad; 25.01.2015
comment
Ой да извините я случайно метод! Должно быть return filter(source) { addedDict.updateValue(true, forKey: $0) == nil }, как вы говорите. - person Airspeed Velocity; 25.01.2015
comment
Это ответ или преобразование в набор быстрее? - person whoKnows; 17.01.2016
comment
Я полагаю, преобразовывая в набор. Я не понимаю, как этот может быть быстрее, поскольку он действительно добавляет каждый элемент в набор с дополнительной логикой сверху. Но такой подход сохраняет исходный порядок, который может быть необходимостью. - person Jean-Philippe Pellet; 17.01.2016
comment
Небольшое предостережение: избегайте обсуждения производительности простых функций, подобных этой, до тех пор, пока вы не будете доказуемо зависеть от их производительности, после чего единственное, что вам следует сделать, - это провести тест. Слишком часто я видел неподдерживаемый код или даже менее производительный код из-за предположений. :) Кроме того, это, наверное, легче понять: let uniques = Array(Set(vals)) - person Blixt; 04.02.2016
comment
@Blixt Согласен. Еще раз, здесь преимущество заключается в соблюдении порядка элементов исходного массива. - person Jean-Philippe Pellet; 04.02.2016
comment
Что было бы наиболее безопасным способом определить этот метод как расширение SequenceType? Это должно быть mutating? Я попробовал, но не получилось: / - person lvp; 08.09.2016
comment
Почему вы ограничиваете T значением Hashable? Я предполагаю, что это потому, что если это не Hashable, вам не разрешено использовать Sets. Я пытаюсь удалить дубликаты из многомерного массива, но поскольку массивы не хэшируемые ... Я думаю, мне следует просто добавить их в массив, а не вставлять. Очевидно, что использование наборов быстрее, потому что оно выигрывает от хеширования ... - person Honey; 10.05.2018
comment
у меня есть [[1,2,4], [1,2,3], [2,3,1], [2,3,4]] такого типа массив, так что мне нужно изменить в вашем коде - person Nirav Hathi; 07.06.2018
comment
@ Jean-PhilippePellet @Blixt Беглый сравнительный анализ: для большого количества элементов все варианты на основе Set оценивались примерно одинаково - лишь незначительные отличия от реализаций здесь (5-10%). Только использование тестов на Dictionary над Set постоянно медленнее (~ 0,5x). Релиз -O, swift 4.1 - person wardw; 22.06.2018
comment
@ Cœur Я написал этот комментарий некоторое время назад. Не лучше ли ограничить это Equatable. Таким образом, он будет более инклюзивным? - person Honey; 15.04.2019
comment
@Honey, если вы используете Equatable, вы не можете использовать Set (вам понадобится Array), и вы проиграете в производительности. Поэтому вам следует добавлять Equatable перегрузку только в том случае, если она вам действительно нужна. Мне было бы любопытно узнать ваш вариант использования. - person Cœur; 15.04.2019
comment
@ Кер, я не помню своего случая. Просто указываю, что эта реализация ограничивает, но, возможно, более оптимизирована. - person Honey; 15.04.2019
comment
Я не вижу использования Set<T> - person preetam; 11.02.2021

Вы можете легко преобразовать в Set и снова обратно в Array:

let unique = Array(Set(originals))

Это не гарантирует сохранение исходного порядка массива.

person Ben Packard    schedule 27.04.2015
comment
Да, после Swift 1.2 это способ решить эту проблему. - person Jim Hillhouse; 20.05.2015
comment
Есть ли способ использовать набор при сохранении исходного порядка массива? - person Crashalot; 27.07.2015
comment
@Crashalot Смотрите мой ответ. - person Jean-Philippe Pellet; 29.07.2015
comment
Если вам нужно сохранить уникальность объектов по определенному свойству, то также реализуйте протокол Hashable и Equatable для этого класса, вместо того, чтобы просто использовать преобразование Array- ›Set-› Array. - person Fawkes; 09.09.2015
comment
Что, если вы хотите полностью удалить дубликаты (а не оставить один из них)? - person Md1079; 20.11.2015
comment
Отлично!! Какова временная сложность этого решения? - person JW.ZG; 31.07.2016
comment
Это заставляет его изменить исходный порядок? - person rr1g0; 15.02.2017
comment
Для Swift3 мне нужно было установить тип для Set ... и привести мою структуру в соответствие с Hashable - person Chris Allinson; 15.03.2017
comment
Это простое решение, но: вы должны соответствовать равноправному протоколу. Изменение Set на Array также не гарантирует сохранения того же порядка элементов. - person Szu; 10.09.2017
comment
Вероятно, это не сохраняет порядок! - person Erik Aigner; 06.09.2018
comment
Это вызывает проблемы на iOS 12. - person Rudolf J; 24.09.2018
comment
@RudolfJ Какие проблемы? - person Ben Packard; 24.09.2018
comment
Ошибка, если элементы в originals не Hashable; только Hashable типы данных могут быть добавлены в набор, но любой тип данных может быть добавлен в массив. - person Mecki; 09.07.2019
comment
Я не понимаю, почему у этого ответа так много голосов. Похоже, что поддержание порядка в массиве почти наверняка является обязательным требованием. В противном случае вы могли бы просто использовать Set вместо Array в первую очередь. - person Christopher Pickslay; 18.06.2020
comment
Основываясь на комментариях к этому ответу, я думаю, что редактирование упорядоченного набора весьма важно ... даже если это явно не задано в вопросе, вероятно, это то, чего хотят многие / большинство людей, нашедших эту страницу. Хорошо иметь этот связанный вариант в ответе с большим количеством голосов, а не намного дальше по странице. - person pkamb; 18.11.2020

Используйте Set или NSOrderedSet для удаления дубликатов, затем конвертируйте обратно в Array:

let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))
person Jovan Stankovic    schedule 28.05.2017
comment
let uniqueOrderedNames = Array (NSOrderedSet (array: userNames)) как! [String] если у вас есть массив String, а не Any - person Zaporozhchenko Oleksandr; 18.06.2018
comment
Ошибка, если элементы в array не Hashable; только Hashable типы данных могут быть добавлены в набор, но любой тип данных может быть добавлен в массив. - person Mecki; 09.07.2019
comment
Протестировано в Swift 5.1b5, учитывая, что элементы являются Hashable и желанием сохранить порядок, NSOrderedSet (array: array) .array незначительно быстрее, чем чистый swift func uniqued (), использующий набор с фильтром. Я тестировал 5100 строк, что дало 13 уникальных значений. - person dlemex; 02.08.2019
comment
Array(NSOrderedSet(array: array)) не работает в Swift 5. Используйте вместо этого NSOrderedSet(array: array).array as! [String]. - person Lindemann; 05.12.2020
comment
Второй работает только для примитивных типов. - person Alejandro L.Rocha; 27.03.2021

Здесь доступно много ответов, но я пропустил это простое расширение, подходящее для Swift 2 и выше:

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

Делает это очень просто. Можно назвать так:

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

Фильтрация на основе свойств

Чтобы отфильтровать массив на основе свойств, вы можете использовать этот метод:

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

Что вы можете назвать следующим образом:

let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }
person Antoine    schedule 26.01.2016
comment
@Antoine Спасибо за фильтрацию на основе расширения свойств. Это действительно полезно. Но не могли бы вы объяснить, как это работает? Для меня это слишком сложно понять. Спасибо - person Mostafa Mohamed Raafat; 04.10.2016
comment
Обновления для swift 3: func filterDuplicates (_ includeElement: (_ lhs: Element, _ rhs: Element) - ›Bool) -› [Element] { - person cbartel; 09.05.2017
comment
Первая часть этого ответа (extension Array where Element: Equatable) заменяется stackoverflow.com/a/36048862/1033581, которая предлагает более мощное решение (extension Sequence where Iterator.Element: Equatable). - person Cœur; 15.06.2017
comment
Это будет иметь O(n²) временную производительность, что очень плохо для больших массивов. - person Duncan C; 03.09.2018
comment
Вы должны использовать набор, чтобы отслеживать элементы, которые вы видели до сих пор, чтобы снизить эту ужасную O(n²) сложность до O(n) - person Alexander; 16.12.2018

Swift 4

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

каждая попытка insert также вернет кортеж: (inserted: Bool, memberAfterInsert: Set.Element). См. документацию.

Использование возвращенного значения означает, что мы можем избежать выполнения более одного цикла, поэтому это O (n).

person mxcl    schedule 22.09.2017
comment
После простого профилирования этот метод работает очень быстро. Это в сотни раз быстрее, чем при использовании reduce (_: _ :) или даже reduce (into: _ :) - person Kelvin; 04.04.2018
comment
@Kelvin Потому что все остальные алгоритмы были O(n^2), и никто этого не заметил. - person Alexander; 16.12.2018
comment
@Kelvin, этот ответ идентичен ответу Энеко Алонсо + моему комментарию (16 июня '17). - person Cœur; 17.12.2018

Если вы поместите оба расширения в свой код, по возможности будет использоваться более быстрая версия Hashable, а версия Equatable будет использоваться в качестве запасного варианта.

public extension Sequence where Element: Hashable {
  /// The elements of the sequence, with duplicates removed.
  /// - Note: Has equivalent elements to `Set(self)`.
  @available(
  swift, deprecated: 5.4,
  message: "Doesn't compile without the constant in Swift 5.3."
  )
  var firstUniqueElements: [Element] {
    let getSelf: (Element) -> Element = \.self
    return firstUniqueElements(getSelf)
  }
}

public extension Sequence where Element: Equatable {
  /// The elements of the sequence, with duplicates removed.
  /// - Note: Has equivalent elements to `Set(self)`.
  @available(
  swift, deprecated: 5.4,
  message: "Doesn't compile without the constant in Swift 5.3."
  )
  var firstUniqueElements: [Element] {
    let getSelf: (Element) -> Element = \.self
    return firstUniqueElements(getSelf)
  }
}

public extension Sequence {
  /// The elements of the sequences, with "duplicates" removed
  /// based on a closure.
  func firstUniqueElements<Hashable: Swift.Hashable>(
    _ getHashable: (Element) -> Hashable
  ) -> [Element] {
    var set: Set<Hashable> = []
    return filter { set.insert(getHashable($0)).inserted }
  }

  /// The elements of the sequence, with "duplicates" removed,
  /// based on a closure.
  func firstUniqueElements<Equatable: Swift.Equatable>(
    _ getEquatable: (Element) -> Equatable
  ) -> [Element] {
    reduce(into: []) { uniqueElements, element in
      if zip(
        uniqueElements.lazy.map(getEquatable),
        AnyIterator { [equatable = getEquatable(element)] in equatable }
      ).allSatisfy(!=) {
        uniqueElements.append(element)
      }
    }
  }
}

Если порядок не важен, вы всегда можете просто использовать этот инициализатор набора .

person Jessy    schedule 05.11.2015
comment
хорошо, понял. я не смог назвать это, потому что мой массив представляет собой массив структур ... как бы я справился с этим в моем случае? структура из 20 различных переменных, строка и [строка] - person David Seek; 21.12.2016
comment
@David Seek Похоже, вы не сделали свой строгий хешируемый или эквивалентный. Это верно? - person Jessy; 21.12.2016
comment
@DavidSeek вот так, uniqueArray = nonUniqueArray.uniqueElements - person Mert Celik; 17.08.2018
comment
да не волнуйся. он заработал сразу после этого. прошло уже почти 2 года: P - person David Seek; 17.08.2018
comment
Это будет иметь O(n²) временную производительность, что очень плохо для больших массивов. - person Duncan C; 03.09.2018
comment
Версия hahsable будет иметь лучшую производительность, но не сохранит порядок элементов в исходном массиве. Ответ Лео даст как O(n) производительность, так и сохранит порядок объектов. - person Duncan C; 03.09.2018
comment
Точнее, он сохраняет исходный порядок массива (каким бы он ни был) и удаляет все, кроме первого появления повторяющихся элементов. - person Duncan C; 03.09.2018
comment
@Alexander Это требует Hashable. Как я утверждаю в своем ответе, моя реализация Hashable из Swift 3 напрямую переводится в Swift 4. Чтобы начало моего ответа было точным, вам необходимо включить решения как для Hashable, так и для Equatable. - person Jessy; 17.12.2018

изменить / обновить Swift 4 или новее

Мы также можем расширить протокол RangeReplaceableCollection, чтобы его можно было использовать с StringProtocol типами:

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"

Способ мутации:

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

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

Для Swift 3 нажмите здесь

person Leo Dabus    schedule 11.01.2016
comment
Мне это нравится, он работает и со множеством словарей! - person DeyaEldeen; 28.09.2016
comment
O (N ^ 2) - это плохо :( - person Alexander; 14.11.2016
comment
@Alexander Leo Dabus заменил реализацию reduce, поэтому теперь сложность другая. - person Cœur; 16.06.2017
comment
Какое выражение .inserted выше? Нигде не могу найти. - person Duncan C; 11.09.2017
comment
@DuncanC - это значение, возвращаемое методом вставки. developer.apple.com/ documentation / swift / set / 1541375-insert возвращает кортеж. (inserted: Bool, memberAfterInsert: Element), если newMember не содержался в наборе. Если элемент, равный newMember, уже содержался в наборе, метод возвращает (false, oldMember), где oldMember - это элемент, равный newMember. В некоторых случаях oldMember можно отличить от newMember путем сравнения идентичности или другими способами. - person Leo Dabus; 11.09.2017
comment
Ага, наконец нашел. Это очень круто. Какова временная сложность вашего решения? На первый взгляд, я думаю, что это будет n • log(n), поскольку наборы используют хеши для определения членства. Наивное решение использования array.contains дает n^2 производительность, что действительно плохо. - person Duncan C; 11.09.2017
comment
@DuncanC Я не знаю сложности, но думаю, что быстрее не может быть - person Leo Dabus; 11.09.2017
comment
Я только что провел быстрый тест на предметах длиной 1 и 8 метров, и, похоже, он прошел O(n) время! - person Duncan C; 11.09.2017
comment
Какой из них быстрее плоская карта или фильтр? Или это то же самое? - person Leo Dabus; 11.09.2017
comment
Результаты интересны. Версия фильтра работает быстрее и для 1 миллиона уникальных элементов, и для 8 миллионов. Однако версия на основе фильтра занимает в 8,38 раза больше времени для 8 миллионов уникальных элементов (волос более O(n) времени), тогда как версия на основе плоской карты занимает в 7,47 раза больше времени для 8 миллионов уникальных записей, чем 1 миллион, что говорит о том, что версия на основе плоской карты масштабируется лучше. Почему-то версия на основе плоской карты работает немного лучше, чем O(n) time! - person Duncan C; 12.09.2017
comment
Фактически, когда я запускаю тест с 64x большим количеством элементов в массиве, версия на основе плоской карты работает быстрее. - person Duncan C; 12.09.2017
comment
@Jessy да, он сохраняет исходный порядок. Это не называется sortedSet. Кстати, он использует то же имя, которое Apple использует в NSOrderedSet, просто отбрасывая NS. - person Leo Dabus; 04.09.2018
comment
Первоначальный порядок - это неопределенная концепция, но вау, вы правы, говоря, что Apple неправильно назвала его. NSOrderedSet(array: [1, 3, 2, 3]) не обеспечивает соблюдение порядка; это [1, 3, 2]. - person Jessy; 04.09.2018
comment
kkkk Почему ты думаешь, что твой путь правильный? Как бы вы это назвали? Обратите внимание, что этот метод не ограничивается числами. Это ограничено Hashable not Comparable - person Leo Dabus; 04.09.2018
comment
Коллекция @Jessy, ordered означает, что независимо от того, сколько раз вы обращаетесь к ней, она будет иметь одинаковый порядок элементов. Как и Array, каждый элемент останется на своем месте. Для Set и Dictionary это неверно, если вы попытаетесь перебрать или распечатать контент, у вас могут быть элементы в разном порядке каждый раз. И этот тип коллекций называется unordered. - person user28434'mstep; 10.04.2019
comment
@DuncanC a filter { expression } является a flatmap { expression ? $0 : nil }, поэтому не должно быть никакой разницы, и я рекомендую filter для удобства чтения. - person Cœur; 12.04.2019

Swift 4

Гарантированно держать заказ.

extension Array where Element: Equatable {
    func removingDuplicates() -> Array {
        return reduce(into: []) { result, element in
            if !result.contains(element) {
                result.append(element)
            }
        }
    }
}
person Alessandro Martin    schedule 11.01.2018
comment
Я использую это сейчас, только изменил название метода на removeDuplicates :) - person J. Doe; 28.03.2018
comment
Я предполагаю, что это решение компактно, но я считаю, что решение deanWombourne, опубликованное годом ранее, может быть немного более эффективным < / b>, чем reduce: в целом, это всего лишь одна строка во всем вашем проекте для записи вашей функции как: var unique: [Iterator.Element] = []; for element in self where !unique.contains(element) { unique.append(element) }; return unique. Признаюсь, я еще не проверял относительные характеристики. - person Cœur; 21.08.2018
comment
Это будет иметь O(n²) временную производительность, что очень плохо для больших массивов. - person Duncan C; 03.09.2018
comment
@NickGaens Нет, это O(n²). В этом нет ничего быстрого. - person Alexander; 16.12.2018
comment
@ Cœur reduce или reduce(into:) не имеют значения. Если переписать это так, чтобы не вызывать повторно contains, то разница будет НАМНОГО больше. - person Alexander; 16.12.2018
comment
@ Cœur Конечно, можешь. На самом деле это довольно просто. Вы просто фильтруете элементы массива в зависимости от того, видели ли вы их уже (что вы поддерживаете, используя набор для O(1) insert и contains). См. stackoverflow.com/a/46354989/3141234 - person Alexander; 17.12.2018
comment
@ Александр извини, да, я отвлекся. Я даже нашел это решение ДО mxcl. - person Cœur; 17.12.2018
comment
Да, как указывалось во многих комментариях, это не очень эффективно по времени, и, если элементы также являются Hashable, другие решения более эффективны. - person Alessandro Martin; 02.03.2020

На основе https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift, мы можем объявить более мощный инструмент, который может фильтровать уникальность по любому ключевому пути. Благодаря комментариям Александра к различным ответам относительно сложности, приведенные ниже решения должны быть близки к оптимальным.

Не мутирующее решение

Мы расширяем его функцией, которая может фильтровать уникальность по любому 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 }
    }
}

Примечание: в случае, если ваш объект не соответствует RangeReplaceableCollection, но соответствует Sequence, у вас может быть это дополнительное расширение, но возвращаемый тип всегда будет массивом:

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

использование

Если мы хотим уникальности для самих элементов, как в вопросе, мы используем 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] */

Если мы хотим уникальности для чего-то еще (например, для id коллекции объектов), мы используем 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}] */

Мутирующее решение

Мы расширяем мутирующую функцию, которая может фильтровать уникальность по любому 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 }
    }
}

использование

Если мы хотим уникальности для самих элементов, как в вопросе, мы используем 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] */

Если нам нужна уникальность для чего-то еще (например, для id коллекции объектов), мы используем 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}] */
person Cœur    schedule 15.04.2019
comment
Вот и хорошая реализация! Я только с этим ключевыми путями можно было преобразовать в замыкания, так что вы можете использовать аргумент замыкания для поддержки как произвольного кода (в замыканиях), так и простого поиска свойств (через ключевые пути). Единственное изменение, которое я хотел бы сделать, - это установить keyPath по умолчанию на \.self, потому что это, вероятно, большинство случаев использования. - person Alexander; 15.04.2019
comment
@Alexander Я попытался установить по умолчанию Self, но тогда мне нужно было сделать Element всегда Hashable. Альтернативой значению по умолчанию является добавление простой перегрузки без параметров: extension Sequence where Element: Hashable { func unique() { ... } } - person Cœur; 15.04.2019
comment
Ах да, имеет смысл! - person Alexander; 15.04.2019
comment
Великолепно ... просто и, главное, «гибко». Спасибо. - person BonanzaDriver; 02.10.2019
comment
@ Alexander-ReinstateMonica: это очень похоже на ваше собственное решение от марта 2018 года: gist.github.com/amomchilov / fbba1e58c91fbd4b5b767bcf8586112b ???????????? - person FizzBuzz; 03.07.2020
comment
Ах, @FizzBuzz, это хорошая находка. Поздравляем Мартина Р. и Александра за то, что они, как и Джон Санделл, являются авангардистами этих техник. - person Cœur; 03.07.2020
comment
@FizzBuzz Я понятия не имею, как вы это обнаружили, но было интересно посмотреть. Я забываю, что вся эта случайная чушь, которую я выкладываю в Интернете, остается повсюду и, в конце концов, попадается на глаза людям. Я могу только надеяться, что это помогло: p - person Alexander; 03.07.2020

Вот категория на SequenceType, которая сохраняет исходный порядок массива, но использует Set для поиска contains, чтобы избежать O(n) затрат на метод contains(_:) массива.

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

Если вы не Hashable или Equatable, вы можете передать предикат для проверки равенства:

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

Теперь, если у вас нет Hashable, но Equatable, вы можете использовать этот метод:

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

Наконец, вы можете добавить версию uniqued по ключевому пути следующим образом:

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

Вы можете вставить оба из них в свое приложение, Swift выберет правильный вариант в зависимости от Iterator.Element типа вашей последовательности.

person deanWombourne    schedule 16.03.2016
comment
Привет, наконец-то, у кого-то есть O(n) решение. Кстати, вы можете объединить операции проверки и вставки множества в одну. См. stackoverflow.com/a/46354989/3141234 - person Alexander; 16.12.2018
comment
Ой, это умно :) - person deanWombourne; 18.12.2018

Альтернативное (если не оптимальное) решение от здесь с использованием неизменяемых типов, а не переменных:

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
}

Включено, чтобы противопоставить императивный подход Жана-Пиллиппа функциональному подходу.

В качестве бонуса эта функция работает как со строками, так и с массивами!

Изменить: этот ответ был написан в 2014 году для Swift 1.0 (до того, как Set был доступен в Swift). Он не требует соответствия Hashable и работает за квадратичное время.

person Pliskin    schedule 20.11.2014
comment
Остерегайтесь, существует не один, а два способа выполнения этого за квадратичное время - и contains, и добавление массива выполняются за O (n). Хотя у него есть то преимущество, что он требует только равноправного, а не хешируемого. - person Airspeed Velocity; 23.12.2014
comment
это действительно сложный способ написания filter. Это O (n ^ 2) (что требуется, если вы не хотите требовать соответствия Hashable), но вы должны, по крайней мере, вызвать это явно - person Alexander; 17.12.2018

быстрый 2

с ответом функции 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
    })
}

использовать:

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
person Daniel Krom    schedule 11.10.2015
comment
Очевидно, что значение Bool является избыточным, поскольку ваш код никогда его не читает. Используйте Set вместо Dictionary, и вы получите мой голос за. - person Nikolai Ruhe; 08.02.2016

Еще одно решение Swift 3.0 для удаления дубликатов из массива. Это решение улучшает многие другие решения, уже предложенные:

  • Сохранение порядка элементов во входном массиве
  • Линейная сложность O (n): однопроходный фильтр O (n) + вставка набора O (1)

Учитывая целочисленный массив:

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]

Функциональный код:

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]

Код расширения массива:

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]

Этот код использует результат, возвращаемый операцией insert на Set, которая выполняется на O(1), и возвращает кортеж, указывающий, был ли элемент вставлен или уже существовал в наборе.

Если элемент был в наборе, filter исключит его из окончательного результата.

person Eneko Alonso    schedule 14.03.2017
comment
Чтобы не быть придирчивым, вы будете выполнять вставку и проверку членства столько раз, сколько есть элементов, поэтому вам также следует посчитать их стоимость как O (n). Однако это не означает 3xO (n), потому что эти O и не имеют одинаковой стоимости с фильтром, поэтому добавление O (n) - это яблоки к апельсинам. Если мы рассматриваем операции набора как часть стоимости фильтра, равную O (1), сложность составляет всего O (n), хотя и с большим O. Нажимая это до предела, вы также можете избежать вставок, когда элемент уже в комплекте. - person Alain T.; 16.03.2017
comment
Вы правы, используя defer, код будет выполнять заданную тестовую операцию дважды: один с contains и один с insert. Прочитав документацию Swift, я обнаружил, что insert возвращает кортеж, указывающий, был ли элемент вставлен, поэтому я упростил код, удалив проверку contains. - person Eneko Alonso; 16.03.2017
comment
Отлично. Ваше расширение может быть оптимальным, если оно будет выполнено на extension Sequence where Iterator.Element: Hashable { ... } - person Cœur; 16.06.2017
comment
@AlainT. Неа. И insert, и contains имеют O(1) сложность. O(1) + O(1) = O(1). Эти две операции затем выполняются n раз (один раз за вызов закрытия, переданного в filter, которое вызывается один раз для каждого элемента), т.е. если операция занимает постоянное количество времени независимо от размера ввода, то выполнение ее дважды по-прежнему требует постоянного времени, независимо от размера ввода. Общая сложность этого O(n). - person Alexander; 16.12.2018

Swift 5

extension Sequence where Element: Hashable {
    func unique() -> [Element] {
        NSOrderedSet(array: self as! [Any]).array as! [Element]
    }
}
person blackjacx    schedule 02.08.2018
comment
Я сделал несколько вариаций, чтобы выбрать ключ для сравнения. 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 } } } - person Marcelo de Aguiar; 07.08.2018
comment
Нет необходимости использовать Bool, когда единственное значение, которое вы используете, - true. Вы приближаетесь к типу единицы (типу только с одним возможным значением). Тип модуля Swift - Void, единственное значение которого - () (также известный как пустой кортеж). Так что вы можете просто использовать [T: Void]. Хотя вам не стоит этого делать, потому что вы, по сути, только что изобрели Set. Вместо этого используйте Set. См. stackoverflow.com/a/55684308/3141234 Удалите этот ответ. - person Alexander; 15.02.2020
comment
если ваш элемент Hasable, вы можете напрямую использовать Array(Set(yourElements) - person Alejandro L.Rocha; 27.03.2021

Думайте как функциональный программист :)

Чтобы отфильтровать список на основе того, появился ли элемент уже, вам нужен индекс. Вы можете использовать enumerated для получения индекса и map для возврата к списку значений.

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

Это гарантирует порядок. Если вас не волнует порядок, то существующий ответ Array(Set(myArray)) проще и, вероятно, более эффективен.


ОБНОВЛЕНИЕ: несколько замечаний по эффективности и правильности

Несколько человек прокомментировали эффективность. Я определенно учусь сначала писать правильный и простой код, а потом выяснять узкие места, хотя я понимаю, что это спорный вопрос, яснее ли это, чем Array(Set(array)).

Этот метод намного медленнее, чем Array(Set(array)). Как отмечено в комментариях, он сохраняет порядок и работает с элементами, которые не являются хэшируемыми.

Однако метод @Alain T также сохраняет порядок и работает намного быстрее. Поэтому, если ваш тип элемента не является хешируемым или вам просто нужен быстрый лайнер, я бы предложил использовать их решение.

Вот несколько тестов на MacBook Pro (2014 г.) на Xcode 11.3.1 (Swift 5.1) в режиме выпуска.

Функция профилировщика и два метода для сравнения:

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

И небольшое разнообразие тестовых входов:

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

Выдает в качестве вывода:

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
person Tim MB    schedule 16.05.2019
comment
в отличие от Array(Set(myArray)), это работает для вещей, которые не Hashable - person Porter Child; 21.10.2019
comment
... и в отличие от Array(Set(myArray)) порядок вашего массива сохраняется. - person Sander Saelmans; 10.02.2020
comment
Похоже, это лучший ответ для меня, по крайней мере, в настоящее время, когда Swift 5 уже является текущей версией. - person oradyvan; 13.02.2020
comment
Это очень элегантное решение; к сожалению, это тоже довольно медленно. - person Colin Stark; 14.02.2020
comment
Это очень медленно. Если бы вы хотели сохранить последний элемент каждого элемента, вы бы лучше пользовались лучшей реализацией и делали array.reversed().unique().reversed(). - person Alexander; 15.02.2020
comment
@ Alexander-ReinstateMonica Я не понимаю вашего предложения array.reversed().unique().reversed(). Вышеупомянутое решение сохраняет первый, а не последний из каждого элемента, и, насколько я знаю, нет метода unique() - в этом весь смысл вопроса. В любом случае, я не сомневаюсь, что это, вероятно, не самый быстрый метод, но, если он не зависит от производительности, я бы предпочел удобочитаемость и ясность правильности оптимизации. - person Tim MB; 16.02.2020
comment
@TimMB О, я неправильно прочитал ваш пост. Я видел чью-то адаптацию, в которой использовался lastIndex(of:). Я полностью не согласен по поводу ясности и оптимизации в этом случае. Я не думаю, что эта реализация особенно ясна, особенно по сравнению с простым решением на основе наборов. В любом случае такой код следует извлечь в функцию расширения. Этот алгоритм становится практически непригодным для использования даже при небольшом размере ввода, например, от тысяч до десятков тысяч. Такие наборы данных найти несложно, у людей могут быть тысячи песен, файлов, контактов и т. Д. - person Alexander; 16.02.2020
comment
@TimMB Вы можете достичь той же цели - выделить не-Hashable элементы, сохранив первые, используя Array для contains проверок, аналогично тому, как используется набор. Это устраняет необходимость в перечислении (что на самом деле не требует больших затрат) и последнем вызове map (который выполняет одну полную развертку всего массива). Кроме того, вы можете использовать .reserveCapacity в наборе / массиве, чтобы компенсировать использование некоторой дополнительной памяти для устранения необходимости перераспределения в середине алгоритма. - person Alexander; 17.02.2020
comment
@TimMB Другие вещи, которые следует учитывать: method3 позволяет реализовать, удаляющие встроенные элементы из существующего буфера с помощью removeAll, что невозможно с методами 1 или 2. Методы 2 и 3 также могут быть обобщены на последовательности, не являющиеся Collection (которые может не повторяться) - person Alexander; 17.02.2020
comment
Посмотрите мои результаты тестов: drive.google .com / a / ryerson.ca / file / d / полный код: gist.github. com / amomchilov / 299d012dccba375bf15880355684ebed - person Alexander; 18.02.2020
comment
Это абсолютное золото - person Jas_meet; 28.04.2020
comment
Следует отметить, что самым быстрым решением (после несортированного набора) действительно может быть Alain T. Подходите к вашему добавлению reserveCapacity, как указано в сути. - person Frederik Winkelsdorf; 05.11.2020

В Swift 5

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

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

Выход будет

 ["Sumit", "Mohan", "Amit", "Aman"]
person Sanjay Mishra    schedule 07.10.2019
comment
Это повторение многих ответов, которые уже были здесь, и оно не сохраняет порядок. - person Alexander; 15.02.2020

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

использование:

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

or

["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
person Rok Gregorič    schedule 12.06.2018
comment
Это O(n^2). Не делай этого. - person Alexander; 16.12.2018

Для массивов, элементы которых не являются ни хэшируемыми, ни сопоставимыми (например, сложные объекты, словари или структуры), это расширение предоставляет обобщенный способ удаления дубликатов:

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

Вам не нужно беспокоиться о создании значений Hashable, и он позволяет вам использовать различные комбинации полей для уникальности.

Примечание. Для более надежного подхода см. решение, предложенное Coeur, в комментариях ниже.

stackoverflow.com/a/55684308/1033581

[EDIT] альтернатива Swift 4

В Swift 4.2 вы можете использовать класс Hasher для более простого создания хеша. Вышеупомянутое расширение можно изменить, чтобы использовать это:

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

Синтаксис вызова немного отличается, потому что закрытие получает дополнительный параметр, содержащий функцию для хеширования переменного количества значений (которые должны быть хэшируемыми индивидуально)

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

Он также будет работать с одним значением уникальности (используя $ 1 и игнорируя $ 0).

peopleArray = peopleArray.filterDuplicate{ $1.name } 
person Alain T.    schedule 16.03.2017
comment
Это может дать случайные результаты в зависимости от поведения "\()", так как это может не дать вам уникальных значений, таких как соответствие Hashable должно. Например, если ваши элементы соответствуют Printable, все возвращая одно и то же description, тогда ваша фильтрация не выполняется. - person Cœur; 16.06.2017
comment
Согласовано. Это необходимо учитывать при выборе полей (или формулы), которые будут создавать желаемый шаблон уникальности. Для многих случаев использования это обеспечивает простое специальное решение, которое не требует изменения класса или структуры элемента. - person Alain T.; 16.06.2017
comment
@AlainT. Не делай этого, правда. Строка не предназначена для создания специального механизма генерации ключей гетто. Просто ограничьте T быть Hashable. - person Alexander; 16.12.2018
comment
@Alexander Я применил эту идею в новом ответе: stackoverflow.com/a/55684308/1033581 - person Cœur; 15.04.2019
comment
Идеальный ответ, как я хочу. Большое спасибо. - person Hardik Thakkar; 07.06.2019

Вы можете использовать непосредственно набор наборов для удаления дубликатов, а затем вернуть их в массив

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]

Затем вы можете заказать свой массив по своему усмотрению

myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]
person Vincent Choubard    schedule 09.05.2015
comment
Затем вы можете упорядочить свой массив по своему усмотрению. Что делать, если мне нужен такой же порядок, как в исходном массиве? Это не так просто. - person Alexander; 17.12.2018

Если вам нужны отсортированные значения, это работает (Swift 4)

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

person Mauricio Chirino    schedule 06.04.2018
comment
В этом случае вы теряете порядок элементов. - person Shmidt; 16.11.2018
comment
Вовсе нет, это то, для чего стоит .sorted() в конце. С Уважением. - person Mauricio Chirino; 19.11.2018
comment
@MauricioChirino А если исходный массив был [2, 1, 1]? Вылезло бы [1, 2], это не заказано: p - person Alexander; 16.12.2018
comment
@MauricioChirino Нет, не будет. Правильный ответ будет [2, 1], но это вернет [1, 2], что неверно. - person Alexander; 20.12.2018
comment
Я думаю, вы путаете сортировку с обратным @Alexander. - person Mauricio Chirino; 26.12.2018
comment
@MauricioChirino Нет, это не так. Если цель состоит в том, чтобы удалить повторяющиеся значения из последовательности, сохраняя при этом порядок, в котором элементы однозначно появляются, этого не происходит. Очень четкий пример счетчика - [2, 1, 1] . Первое появление уникальных элементов в порядке [2, 1]. Это правильный ответ. Но, используя ваш (неправильный) алгоритм, вы получите [1, 2], который отсортирован, но не в правильном, исходном порядке. - person Alexander; 27.12.2018
comment
Вы правы, я отредактировал ответ. С уважением @Alexander - person Mauricio Chirino; 28.12.2018
comment
Ошибка, если элементы в array не Hashable; только Hashable типы данных могут быть добавлены в набор, но любой тип данных может быть добавлен в массив. - person Mecki; 09.07.2019

Чуть более сжатая синтаксическая версия ответа Даниэля Крома Swift 2, использующая завершающее закрытие и сокращенное имя аргумента, которое, похоже, основано на на исходном ответе 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 }
}

Пример реализации настраиваемого типа, который может использоваться с uniq(_:) (который должен соответствовать Hashable и, следовательно, Equatable, потому что Hashable расширяет 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
  }

}

В приведенном выше коде ...

id, используемый в перегрузке ==, может быть любым Equatable типом (или методом, возвращающим тип Equatable, например, someMethodThatReturnsAnEquatableType()). Закомментированный код демонстрирует расширение проверки на равенство, где someOtherEquatableProperty - другое свойство типа Equatable (но также может быть методом, возвращающим тип Equatable).

id, используемый в вычисляемом свойстве hashValue (требуется для соответствия Hashable), может быть любым Hashable (и, следовательно, Equatable) свойством (или методом, возвращающим тип Hashable).

Пример использования 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
person Scott Gardner    schedule 29.11.2015
comment
Нет необходимости использовать Bool, когда единственное значение, которое вы используете, - true. Вы приближаетесь к типу единицы (типу только с одним возможным значением). Тип модуля Swift - Void, единственное значение которого - () (также известный как пустой кортеж). Так что вы можете просто использовать [T: Void]. Хотя вам не стоит этого делать, потому что вы, по сути, только что изобрели Set. Вместо этого используйте Set. См. stackoverflow.com/a/55684308/3141234 - person Alexander; 15.02.2020

  1. Сначала добавьте все элементы массива в NSOrderedSet.
  2. Это удалит все дубликаты в вашем массиве.
  3. Снова преобразуйте этот упорядоченный набор в массив.

Выполнено....

Пример

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

вывод arrayWithoutDuplicates - [1,2,4,6,8]

person Mahendra Thotakura    schedule 22.07.2019

Вот решение, которое

  • Не использует устаревшие NS типы
  • Достаточно быстро с O(n)
  • Кратко
  • Сохраняет порядок элементов
extension Array where Element: Hashable {

    var uniqueValues: [Element] {
        var allowed = Set(self)
        return compactMap { allowed.remove($0) }
    }
}
person Erik Aigner    schedule 02.04.2020
comment
Это хорошо, но работает только для элементов Hashable. - person Alejandro L.Rocha; 27.03.2021

здесь я сделал решение O (n) для объектов. Не многострочное решение, но ...

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")
person kas-kad    schedule 03.10.2015
comment
Вместо того, чтобы использовать Set с настраиваемым DistinctWrapper, вы должны использовать Dictionary от различных атрибутов к объектам. Следуя этой логике, вы в конечном итоге реализуете [_4 _] https://pastebin.com/w90pVe0p(https://developer.apple.com/documentation/swift/dictionary/2995340-init), который теперь встроен в стандартную библиотеку. Посмотрите, насколько это просто pastebin.com/w90pVe0p - person Alexander; 17.12.2018

Я использовал ответ @Jan-Philippe Pellet и сделал расширение Array, которое выполняет операции, подобные множеству, с массивами, сохраняя при этом порядок элементов.

/// 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) })
  }
}
person Will Richardson    schedule 21.11.2015
comment
Нет необходимости использовать Bool, когда единственное значение, которое вы используете, - true. Вы приближаетесь к типу единицы (типу только с одним возможным значением). Тип модуля Swift - Void, единственное значение которого - () (также известный как пустой кортеж). Так что вы можете просто использовать [T: Void]. Хотя вам не стоит этого делать, потому что вы, по сути, только что изобрели Set. Вместо этого используйте Set. См. stackoverflow.com/a/55684308/3141234 - person Alexander; 15.02.2020

Это просто очень простая и удобная реализация. Вычисляемое свойство в расширении массива, которое имеет равноправные элементы.

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
    }
}
person DaveAMoore    schedule 09.06.2016
comment
Это тоже O(n^2). - person Alexander; 16.12.2018

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

Использование:

let f = removeDublicate(ab: [1,2,2])
print(f)
person Jack Rus    schedule 02.01.2017
comment
Я думаю это самый простой - person Jack Rus; 02.01.2017
comment
он сохраняет порядок и дает вам нужный массив - person Jack Rus; 02.01.2017
comment
Это тоже O(n²). - person Alexander; 16.12.2018

Swift 3 / Swift 4 / Swift 5

Всего одна строка кода, чтобы исключить дубликаты массива без изменения порядка:

let filteredArr = Array(NSOrderedSet(array: yourArray))
person vilas deshmukh    schedule 14.03.2020
comment
Здесь мы преобразуем массив в Orderedset. Определение наборов - наборы допускают только различные значения (не допускают дублирования). Следовательно, дубликаты будут опущены. Поскольку мы приводим типы с помощью NSOrderedSet, порядок массива не будет нарушен. - person vilas deshmukh; 14.03.2020

Самый простой способ - использовать NSOrderedSet, который хранит уникальные элементы и сохраняет порядок элементов. Нравиться:

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)
person sgl0v    schedule 25.04.2018
comment
Интересно, как эта производительность сравнивается с лучшими ответами здесь. Вы сравнивали? - person Alexander; 16.12.2018

Я думаю, что это лучший способ знать саму логику

var arrayOfInts = [2, 2, 4, 4]
var mainArray = [Int]()

for value in arrayOfInts {

if mainArray.contains(value) != true  {
    
    mainArray.append(value)
    print("mainArray:\(mainArray)")
}}
person Deepak Yadeedya    schedule 24.11.2019
comment
Это квадратичное поведение. Каждая итерация вашего вызова цикла содержит, который сам использует цикл по всем элементам. Действительно медленно. - person Alexander; 15.02.2020
comment
mainArray.contains (value) == false можно упростить до mainArray.contains (value)! = true - person Tamim Attafi; 16.07.2020

Как было отмечено на WWDC 2021, Swift имеет разработанные сообществом алгоритмы, коллекции и числовые пакеты. Пакет Algorithms содержит алгоритм uniqued().

Они еще не входят в стандартную библиотеку Swift. В настоящее время вы можете загрузить их со страницы Apple на Github и / или установить через Swift Package Manager.

Видео WWDC:

https://developer.apple.com/videos/play/wwdc2021/10256/ < / а>

Страница Github:

https://github.com/apple/swift-algorithms

uniqued() и uniqued(on:) документация:

https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md

person MH175    schedule 25.06.2021

Вы всегда можете использовать Словарь, потому что Словарь может содержать только уникальные значения. Например:

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

Как видите, результирующий массив не всегда будет в «порядке». Если вы хотите отсортировать / упорядочить массив, добавьте это:

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

.

person AT3D    schedule 02.04.2015

Я считаю, что было бы хорошо предложить функции uniq() и uniqInPlace() для изменения массива путем удаления его значений. Это работает аналогично функциям sort() и sortInPlace(), предоставляемым Swift. Кроме того, поскольку это массив, он должен сохранять исходный порядок элементов.

extension Array where Element: Equatable {

    public func uniq() -> [Element] {
        var arrayCopy = self
        arrayCopy.uniqInPlace()
        return arrayCopy
    }

    mutating public func uniqInPlace() {
        var seen = [Element]()
        var index = 0
        for element in self {
            if seen.contains(element) {
                removeAtIndex(index)
            } else {
                seen.append(element)
                index++
            }
        }
    }
}

Вы можете использовать uniqInPlace() только в массиве переменных (т. Е. var), поскольку вы не можете изменять постоянный массив (т. Е. let).

Некоторые примеры использования:

var numbers = [1, 6, 2, 2, 4, 1, 5]
numbers.uniqInPlace() // array is now [1, 6, 2, 4, 5]

let strings = ["Y", "Z", "A", "Y", "B", "Y", "Z"]
let uniqStrings = strings.uniq() // uniqStrings is now ["Y", "Z", "A", "B"]
person lammert    schedule 18.12.2015
comment
Array<Element> не подходит для типа seen. Повторяющиеся contains вызовов (каждый из которых O(n)) делают этот алгоритм не менее O(n^2). Кроме того, removeAtIndex) также O(n) (потому что он заставляет каждый элемент после элемента remove сдвигаться влево на 1). Вместо этого подобный алгоритм будет лучше работать с var seen = Set<Element>(), и вместо удаления элементов вы перезаписываете их, читая заранее, пока не увидите следующий элемент, который вам нужно сохранить. - person Alexander; 16.12.2018
comment
Таким образом, вы сохраняете все элементы, которые хотите, и в конечном итоге получаете набор пустых пространств в конце массива, которые можно просто обрезать одним выстрелом. - person Alexander; 16.12.2018

Позвольте мне предложить ответ, аналогичный ответу Скотта Гарднера, но с более лаконичным синтаксисом с использованием сокращения. Это решение удаляет дубликаты из массива настраиваемых объектов (сохраняя исходный порядок)

// Custom Struct. Can be also class. 
// Need to be `equitable` in order to use `contains` method below
struct CustomStruct : Equatable {
      let name: String
      let lastName : String
    }

// conform to Equatable protocol. feel free to change the logic of "equality"
func ==(lhs: CustomStruct, rhs: CustomStruct) -> Bool {
  return (lhs.name == rhs.name && lhs.lastName == rhs.lastName)
}

let categories = [CustomStruct(name: "name1", lastName: "lastName1"),
                  CustomStruct(name: "name2", lastName: "lastName1"),
                  CustomStruct(name: "name1", lastName: "lastName1")]
print(categories.count) // prints 3

// remove duplicates (and keep initial order of elements)
let uniq1 : [CustomStruct] = categories.reduce([]) { $0.contains($1) ? $0 : $0 + [$1] }
print(uniq1.count) // prints 2 - third element has removed

И просто, если вам интересно, как работает эта магия сокращения - здесь точно то же самое, но с использованием более расширенного синтаксиса сокращения

let uniq2 : [CustomStruct] = categories.reduce([]) { (result, category) in
  var newResult = result
  if (newResult.contains(category)) {}
  else {
    newResult.append(category)
  }
  return newResult
}
uniq2.count // prints 2 - third element has removed

Вы можете просто скопировать и вставить этот код в Swift Playground и поэкспериментировать.

person n0_quarter    schedule 13.07.2016
comment
Для Swift 4 вам нужно заменить $0 на $0.0 и $1 на $0.1. - person Cœur; 15.06.2017
comment
Обратите внимание, что до Swift 4 (то есть в Swift 3) не рекомендуется использовать reduce с изменяемым типом структуры (здесь это []), поскольку изменчивость не сохраняется, а reduce создает накладные расходы на копирование структуры каждый раз. - person Cœur; 15.06.2017
comment
Как правило, вам нужно инкапсулировать такой код в функцию, а не копировать и вставлять его везде, где он используется. И reduce не подходит для этого алгоритма. Это делает это O(n²) - person Alexander; 16.12.2018

В Swift 3.0 я нашел самое простое и быстрое решение для устранения повторяющихся элементов при сохранении порядка:

extension Array where Element:Hashable {
    var unique: [Element] {
        var set = Set<Element>() //the unique list kept in a Set for fast retrieval
        var arrayOrdered = [Element]() //keeping the unique list of elements but ordered
        for value in self {
            if !set.contains(value) {
                set.insert(value)
                arrayOrdered.append(value)
            }
        }

        return arrayOrdered
    }
}
person Ciprian Rarau    schedule 18.12.2016
comment
Вы можете сделать contains и insert за один раз: stackoverflow.com/a/46354989/3141234 - person Alexander; 16.12.2018

Вот более гибкий способ сделать последовательность уникальной с помощью настраиваемой функции сопоставления.

extension Sequence where Iterator.Element: Hashable {

    func unique(matching: (Iterator.Element, Iterator.Element) -> Bool) -> [Iterator.Element] {

        var uniqueArray: [Iterator.Element] = []
        forEach { element in
            let isUnique = uniqueArray.reduce(true, { (result, item) -> Bool in
                return result && matching(element, item)
            })
            if isUnique {
                uniqueArray.append(element)
            }
        }
        return uniqueArray
    }
}
person MarkHim    schedule 19.02.2018
comment
Это интересная демонстрация того, как contains(where:) может быть реализовано только с reduce, но в производственном коде, подобном этому, лучше просто использовать contains(where:) напрямую. Во-первых, он может закоротить поиск, если элемент найден на ранней стадии (нет необходимости продолжать поиск после этого, но этот метод reduce все равно делает это) - person Alexander; 16.12.2018

это самый простой способ в Swift 4.2 и далее код, как показано ниже

let keyarray:NSMutableArray = NSMutableArray()

for  object in dataArr
{
    if !keysArray.contains(object){
        keysArray.add(object)
    }
}

print(keysArray)
person Deepak Yadeedya    schedule 06.09.2018
comment
Уф. Не делай этого. Это алгоритм O(n^2) (потому что contains равно O(n), который сам выполняется n раз). И не используйте NSMutableArray в Swift - person Alexander; 16.12.2018

Xcode 10.1 - Простое и мощное решение Swift 4.2

func removeDuplicates(_ nums: inout [Int]) -> Int {
    nums = Set(nums).sorted()
    return nums.count
}

Пример

var arr = [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9]
removeDuplicates(&arr)

print(arr) // [1,2,3,4,5,6,7,8,9]
person Saranjith    schedule 17.01.2019
comment
При этом не сохраняется исходный порядок: применяется новый порядок, который может быть, а может и не быть таким же. Даже если это тот же порядок, он не так эффективен, как решение, которое просто сохранит существующий порядок без добавления дополнительного sorted. - person Cœur; 15.04.2019
comment
Да, звонок sorted() - это совершенно неуместная ответственность. Вызывающий запросил дедупликацию. Если им также нужна сортировка, они уже могут это сделать сами. Я предлагаю удалить этот ответ. - person Alexander; 15.02.2020

Мое решение, кажется, может быть в O (n) времени, поскольку доступ к хэш-карте - O (1), а фильтр - O (n). Он также использует закрытие для выбора свойства, по которому следует различать элементы в последовательности.

extension Sequence {

    func distinct<T: Hashable>(by: (Element) -> T) -> [Element] {
        var seen: [T: Bool] = [:]
        return self.filter { seen.updateValue(true, forKey: by($0)) == nil }
    }
}
person Michał Ziobro    schedule 15.07.2019
comment
Нет необходимости использовать Bool, когда единственное значение, которое вы используете, - true. Вы приближаетесь к типу единицы (типу только с одним возможным значением). Тип модуля Swift - Void, единственное значение которого - () (также известный как пустой кортеж). Так что вы можете просто использовать [T: Void]. Хотя вам не следует этого делать, потому что вы, по сути, только что изобрели Set. Вместо этого используйте Set. См. stackoverflow.com/a/55684308/3141234 Удалите этот ответ. - person Alexander; 15.02.2020

Я создал функцию высшего порядка с временной сложностью o (n). Кроме того, такая возможность, как карта, возвращать любой тип, который вы хотите.

extension Sequence {
    func distinct<T,U>(_ provider: (Element) -> (U, T)) -> [T] where U: Hashable {
        var uniqueKeys = Set<U>()
        var distintValues = [T]()
        for object in self {
            let transformed = provider(object)
            if !uniqueKeys.contains(transformed.0) {
                distintValues.append(transformed.1)
                uniqueKeys.insert(transformed.0)
            }
        }
        return distintValues
    }
}
person Abhijit    schedule 18.07.2019
comment
Это делает две операции хеширования для каждого элемента, что не нужно. insert возвращает кортеж, который сообщает вам, был ли элемент уже там или был добавлен впервые. stackoverflow.com/a/55684308/3141234 Удалите этот ответ. - person Alexander; 15.02.2020

если вы хотите сохранить порядок, используйте это

let fruits = ["apple", "pear", "pear", "banana", "apple"] 
let orderedNoDuplicates = Array(NSOrderedSet(array: fruits).map({ $0 as! String }))
person Habib Ali    schedule 18.11.2020

Сохранять уникальные значения и сохранять сортировку в массиве.

(с использованием Swift 3)

    var top3score: [Int] = []


    outerLoop: for i in 0..<top10score.count {
        dlog(message: String(top10score[i]))

        if top3score.count == 3 {
            break
        }

        for aTop3score in top3score {
            if aTop3score == top10score[i] {
                continue outerLoop
            }
        }

        top3score.append(top10score[i])

    }

    print("top10score is \(top10score)")  //[14, 5, 5, 5, 3, 3, 2, 2, 2, 2]
    print("top3score is \(top3score)")   //[14, 5, 3]
person oOEric    schedule 25.03.2017
comment
Это хороший первый подход, но он довольно сложный и многословный. Этот код выполняет слишком много функций (дедупликация, усечение до 3 результатов), и нет простого способа повторно использовать его без массового копирования / вставки. Вам следует взглянуть на этот ответ. Тогда вы могли бы написать что-нибудь вроде scores.uniqued().prefix(3). Простой! - person Alexander; 16.12.2018
comment
Спасибо за ваш комментарий. Основные моменты приведенного выше кода заключаются не только в сохранении уникальных значений, но и в сохранении исходного порядка. Использование Set не может сохранить порядок. :) - person oOEric; 17.12.2018
comment
Да, оно может. Взгляните на ответ, который я могу. Вы используете набор для быстрых ответов на вопрос «Видел ли я этот элемент?» - person Alexander; 17.12.2018

Для этой цели я сделал максимально простое расширение.

extension Array where Element: Equatable {

    func containsHowMany(_ elem: Element) -> Int {
        return reduce(0) { $1 == elem ? $0 + 1 : $0 }
    }

    func duplicatesRemoved() -> Array {
        return self.filter { self.containsHowMany($0) == 1 }
    }

    mutating func removeDuplicates() {
        self = self.duplicatesRemoved(()
    }
}

Вы можете использовать duplicatesRemoved(), чтобы получить новый массив, повторяющиеся элементы которого удалены, или removeDuplicates(), чтобы изменить себя. Видеть:

let arr = [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8]

let noDuplicates = arr.duplicatesRemoved()
print(arr) // [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8]
print(noDuplicates) // [1, 2, 3, 4, 5, 6, 7, 8]

arr.removeDuplicates()
print(arr) // [1, 2, 3, 4, 5, 6, 7, 8]
person Igor Silva    schedule 05.01.2018
comment
Это тоже O(n²). - person Alexander; 16.12.2018
comment
Фактически, он имеет 2 отрицательных голоса и idk. Наверное, потому что это бесполезная квадратичная - person Alexander; 06.07.2021

var numbers = [1,2,3,4,5,10,10, 12, 12, 6,6,6,7,8,8, 8, 8, 8 , 7 , 1 , 1, 2 , 9]

var newArr : [Int] = []
for n in numbers {
    if !newArr.contains(n) {
        newArr.append(n)
    }
}

вывод - [1, 2, 3, 4, 5, 10, 12, 6, 7, 8, 9]

Приведенное выше решение поддерживает порядок, но очень медленно, поскольку .contains повторяется снова и снова. Таким образом используйте заказанный набор.

Это напечатает упорядоченный массив.

Array(NSOrderedSet.init(array: numbers))

вывод - [1, 2, 3, 4, 5, 10, 12, 6, 7, 8, 9]

Это напечатает неупорядоченный массив.

let uniqueUnordered = Array(Set(numbers))

вывод - [4, 2, 1, 9, 10, 3, 5, 6, 8, 12, 7]

person Shrikant Phadke    schedule 10.02.2021

Это работает в Swift 4, если вы не хотите / не должны преобразовывать результат в массив, но можете сделать это с помощью Set. По умолчанию результат не сортируется, но вы можете сделать это с помощью sorted (), который возвращает массив, как показано в операторе печати.

let array = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]

var result = Set<Int>()
_ = array.map{ result.insert($0) }

print(result.sorted())  // [1, 2, 4, 6, 15, 24, 60]
person Johannes    schedule 08.05.2018
comment
Это необратимо теряет упорядоченность. Сортировка имеет смысл только в том случае, если ваш исходный порядок был отсортирован, что не относится к вашему примеру. Кроме того, не злоупотребляйте map там, где forEach имело бы больше смысла. Тем не менее, это могло быть просто let result = Set(array) - person Alexander; 16.12.2018