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

167

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

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

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

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

Обратите внимание, что дубликаты из 2, 6 и 15 были удалены, чтобы убедиться, что существует только один из каждого идентичного элемента. Сможет ли Свифт сделать это легко, или я должен сам это сделать?

  • 9
    Самый простой способ - преобразовать массив в NSSet , NSSet - это неупорядоченная коллекция объектов, если необходимо сохранить порядок NSOrderedSet.
  • 0
    Вы можете использовать функцию пересечения, как вы можете найти в этом классе с функциями для массивов: github.com/pNre/ExSwift/blob/master/ExSwift/Array.swift
Показать ещё 3 комментария
Теги:
arrays
standard-library

37 ответов

84
Лучший ответ

Вы можете бросить свой собственный, например, так (обновлено для Swift 1.2 с Set):

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

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

Версия Swift 3:

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
}
  • 8
    Вы также можете реализовать тело этой функции как var addedDict = [T:Bool](); return filter(source) { addedDict(true, forKey: $0) == nil }
  • 0
    Очень хорошо. Ваше решение будет моим предпочтительным подходом для такого рода ситуаций отныне.
Показать ещё 10 комментариев
352

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

let unique = Array(Set(originals))

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

  • 16
    Это должен быть принятый ответ ура приятель
  • 0
    Да, после Swift 1.2, это способ решения этой проблемы.
Показать ещё 13 комментариев
56

Многие ответы доступны здесь, но я пропустил это простое расширение, подходящее для 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 }
  • 0
    Мне нравится ваш метод фильтрации на основе свойств, он работает! Что происходит с дубликатами и как их поместить в отдельный массив?
  • 0
    @ Antoine Спасибо за фильтрацию по расширению свойств. Это действительно полезно. Но не могли бы вы объяснить, как это работает. Это слишком сложно понять для меня. Спасибо
Показать ещё 4 комментария
47

Это берет некоторую полезную информацию, которая уже есть на этой странице, и применяет подход Hashable/Set, когда это возможно, и в противном случае возвращается к уравнительному коду.

Swift 4 изменений для Equatable расширения (Hashable остается неизменным)

public extension Sequence where Element: Equatable {
  var uniqueElements: [Element] {
    return self.reduce(into: []) {
      uniqueElements, element in

      if !uniqueElements.contains(element) {
        uniqueElements.append(element)
      }
    }
  }
}

Свифт 3

public extension Sequence where Iterator.Element: Hashable {
    var uniqueElements: [Iterator.Element] {
        return Array( Set(self) )
    }
}
public extension Sequence where Iterator.Element: Equatable {
    var uniqueElements: [Iterator.Element] {
        return self.reduce([]){
            uniqueElements, element in

            uniqueElements.contains(element)
            ? uniqueElements
            : uniqueElements + [element]
        }
    }
}

Swift 2

public extension SequenceType where Generator.Element: Hashable {
  var uniqueElements: [Generator.Element] {
    return Array(
      Set(self)
    )
  }
}
public extension SequenceType where Generator.Element: Equatable {
  var uniqueElements: [Generator.Element] {
    return self.reduce([]){uniqueElements, element in
      uniqueElements.contains(element)
        ? uniqueElements
        : uniqueElements + [element]
    }
  }
}
  • 0
    Этот метод работает с Swift 2.2, отличное решение.
  • 3
    Работает, но не соблюдает порядок
Показать ещё 14 комментариев
44

Swift 3.0

let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))
  • 0
    Работая как шарм., Спасибо !!!
  • 0
    Сказочные !!! (Y),
Показать ещё 1 комментарий
32

Ограничение элементов коллекции в Equatable, которое вы можете использовать, содержит:

extension Collection where Element: Equatable {
    var orderedSet: [Element]  {
        var array: [Element] = []
        return compactMap {
            if array.contains($0) {
                return nil
            } else {
                array.append($0)
                return $0
            }
        }
    }
}

Другим вариантом является ограничение элемента коллекции на Hashable и использование набора для управления тем, какие элементы вы должны отобразить в результате:

extension Collection where Element: Hashable {
    var orderedSet: [Element]  {
        var set = Set<Element>()
        return compactMap { set.insert($0).inserted ? $0 : nil }
    }
}

используя фильтр:

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

или используя NSOrderedSet:

extension Array where Element: Hashable {
    var orderedSet: Array {
        return NSOrderedSet(array: self).array as? Array ?? []
    }
}

Используя Swift 4 уменьшить (в :)

extension Collection where Element: Hashable {
    var orderedSet: [Element] {
        var set: Set<Element> = []
        return reduce(into: []) { set.insert($1).inserted ? $0.append($1) : () }
    }
}

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

Вы также можете расширить протокол RangeReplaceableCollection, чтобы он также мог использоваться с типами StringProtocol (Strings и SubStrings):

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

"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"
  • 1
    Мне нравится это, это работает с массивом словарей тоже!
  • 5
    O (N ^ 2) плохо :(
Показать ещё 13 комментариев
24

Swift 4

Гарантированно сохранить порядок.

extension Array where Element: Equatable {
    func removingDuplicates() -> Array {
        return reduce(into: []) { result, element in
            if !result.contains(element) {
                result.append(element)
            }
        }
    }
}
  • 0
    Очень Swifty кусок кода; отлично.
  • 0
    Я использую это сейчас, только изменил имя метода, чтобы удалить дубликаты :)
Показать ещё 6 комментариев
20

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). Смотрите документацию.

Использование возвращаемого значения помогает нам избежать зацикливания или выполнения каких-либо других операций.

  • 7
    После простого профилирования этот метод действительно быстрый. Это в сотни раз быстрее, чем при использовании команды «уменьшить» (_: _ :) или даже уменьшить (в: _ :)
  • 0
    @Kelvin Потому что все эти другие алгоритмы были O(n^2) , и никто не заметил.
Показать ещё 1 комментарий
11

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

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 и работает в квадратичном времени.

  • 7
    Остерегайтесь, есть не один, а два способа, которыми это выполняется за квадратичное время - оба contains и добавление массива выполняется в O (n). Хотя он имеет преимущество только в том, что требует равных, а не хэш.
  • 0
    это действительно сложный способ написания filter . Это O (n ^ 2) (что необходимо, если вы не хотите требовать соответствия Hashable ), но вы должны по крайней мере явно Hashable это
10

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

public extension Array where Element: Hashable {

    /// Return the array 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 comment.
    public func uniqued() -> [Element] {
        var seen = Set<Element>()
        return self.filter { seen.insert($0).inserted }
    }
}

или если у вас нет Hashable, вы можете сделать это:

public extension Sequence where Iterator.Element: Equatable {

    public func uniqued() -> [Iterator.Element] {
        var buffer: [Iterator.Element] = []

        for element in self {
            guard !buffer.contains(element) else { continue }

            buffer.append(element)
        }

        return buffer
    }
}

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

  • 0
    Эй, наконец, кто-то с решением O(n) . Кстати, вы можете объединить операции set проверки и вставки в одну. См. Stackoverflow.com/a/46354989/3141234
  • 0
    О, это умно :)
10

swift 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
  • 0
    Значение Bool очевидно, является избыточным, поскольку ваш код никогда не читает его. Используйте Set вместо Dictionary и вы получите мое повышение.
6

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

или же

["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
  • 0
    для последнего стремительного. его переименуйте в .uniq ()
  • 0
    Это O(n^2) . Не делай этого.
6

Еще одно решение 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]

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

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 исключит его из окончательного результата.

  • 1
    Не будьте разборчивы, но вы будете выполнять вставку и тест членства столько раз, сколько есть элементов, поэтому вы должны также учитывать их стоимость как O (n). Это не означает, что 3xO (n), потому что эти O и не имеют равную стоимость с фильтром, поэтому добавление O (n) - это яблоки к апельсинам. Если мы считаем, что операции над множествами являются O (1) частью стоимости фильтра, сложность просто O (n), хотя и с большим «O». Нажимая на это до предела, вы также можете избежать вставки, когда элемент уже находится в наборе.
  • 0
    Вы правы: при использовании defer код дважды выполняет заданную тестовую операцию, одну с contains а другую с insert . Дальнейшее чтение Swift документации, я обнаружил , что insert возвращает кортеж , указывающий , если элемент был вставлен или нет, поэтому я упростил код Извлечения contains проверку.
Показать ещё 2 комментария
5

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

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]
  • 0
    «Тогда вы можете упорядочить свой массив так, как хотите». Что если я захочу упорядочить то же, что и в исходном массиве? Это не так просто.
4

Swift 4.2 протестирован

extension Sequence where Iterator.Element: Hashable {
    func unique() -> [Iterator.Element] {
        var seen: [Iterator.Element: Bool] = [:]
        return self.filter { seen.updateValue(true, forKey: $0) == nil }
    }
}
  • 0
    Я сделал несколько вариантов, чтобы выбрать ключ для сравнения. 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 } } }
3

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

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

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

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

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

  • 0
    Это может дать случайные результаты в зависимости от поведения "\()" , так как оно может не дать вам уникальные значения, такие как соответствие Hashable . Например, если все ваши элементы соответствуют Printable и все они возвращают одно и то же description , ваша фильтрация завершится неудачно.
  • 0
    Согласовано. Выбор полей (или формулы), которые будут производить желаемый шаблон уникальности, должен будет учитывать это. Для многих случаев использования это обеспечивает простое специальное решение, которое не требует изменения класса или структуры элемента.
Показать ещё 1 комментарий
3

Немного более сжатая версия синтаксиса Даниэль Кром Свифт 2 ответ, используя закрывающее закрытие и сокращенное имя аргумента, которое, по-видимому, основано на Первоначальный ответ скорости воздушной скорости:

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 extends 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
2

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

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

  • 1
    Вы теряете порядок элементов в этом случае.
  • 0
    Вовсе нет, для этого предназначен .sorted() в конце. С уважением.
Показать ещё 6 комментариев
2
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)
  • 0
    Я думаю, что это самый простой
  • 0
    он сохраняет порядок и дает вам массив, который вы хотите
Показать ещё 1 комментарий
2

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

// 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 и поиграть.

  • 0
    Для Swift 4 вам нужно заменить $0 на $0.0 и $1 на $0.1 .
  • 0
    Обратите внимание, что до Swift 4 (то есть в Swift 3) не рекомендуется использовать reduce с изменяемым типом структуры (здесь это []), поскольку изменчивость не сохраняется, а reduce создает накладные расходы на копирование структуры каждый раз.
Показать ещё 1 комментарий
2

Я использовал ответ @Jean-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) })
  }
}
2

здесь я сделал некоторое 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")
  • 0
    Вместо того, чтобы использовать Set с пользовательским DistinctWrapper , вы должны использовать Dictionary от visibleAttributes для объектов. Когда вы будете следовать этой логике, вы в конечном итоге будете реализовывать [ Dictionary.init(_:uniquingKeysWith:) ] pastebin.com/w90pVe0p(https://developer.apple.com/documentation/… , который теперь встроен в Стандартная библиотека. Проверьте, насколько это просто pastebin.com/w90pVe0p
2

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

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

.

1

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

let keyarray:NSMutableArray = NSMutableArray()

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

print(keysArray)
  • 1
    Ooof. Не делай этого. Это алгоритм O(n^2) (потому что contains O(n) , который запускается n раз). И не используйте NSMutableArray в Swift
1

Для этого я сделал простое расширение.

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() чтобы 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]
  • 0
    Это тоже O(n²) .
1

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

Swift 3

На основе ответа Jean-Philippe Pellet, я обновил его синтаксис для Swift 3.

func uniq<S : Sequence, T : Hashable where S.Iterator.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(source: vals) // [1, 4, 2, 6, 24, 15, 60]
1

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

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
    }
}
  • 0
    Вероятно, if условие здесь имеет смысл, чем охранник.
  • 0
    Я согласен, это делает решение более понятным.
Показать ещё 1 комментарий
1

Я считаю, что было бы неплохо предложить функции 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() для переменной Array (т.е. 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"]
  • 0
    Array<Element> не является подходящим выбором для данного типа seen . Повторяющиеся вызовы contains (каждый из которых O(n) ) делает этот алгоритм по крайней мере O(n^2) . Кроме того, removeAtIndex ) также равно O(n) (потому что каждый элемент после элемента удаления должен быть смещен влево на 1). Вместо этого алгоритм, подобный этому, будет работать лучше, используя var seen = Set<Element>() , и вместо того, чтобы «удалять» элементы, вы «перезаписываете» их, читая вперед, пока не увидите следующий элемент, который вам нужно сохранить.
  • 0
    Таким образом, вы сохраняете все нужные элементы и получаете набор пустого пространства в конце массива, который можно просто обрезать за один выстрел
1

Вот ответ, который я придумал после поиска в Интернете и не находил того, что искал. Используя Set, вы можете добавить все элементы с уменьшением. Затем я беру результат и преобразовываю его в отсортированный массив.

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

let distinct2 = initialArray.reduce(Set<Int>(), combine: { (set, current) -> Set<Int> in
    var tmp = set
    tmp.insert(current)
    return tmp
})

// distinct2 is now a set containing {2, 4, 60, 6, 15, 24, 1}

// Make it into a sorted array
let sorted = Array(distinct2).sorted(<) // Returns [1, 2, 4, 6, 15, 24, 60]
0

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

Это работает в Swift 4, если вы не хотите/не хотите конвертировать результат в массив, но можете делать это с множеством. Результат не сортируется по умолчанию, но вы можете сделать это с помощью функции 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]
  • 0
    Это необратимо теряет порядок. Сортировка имеет смысл только в том случае, если исходный порядок сортировки был отсортирован, что не соответствует вашему примеру. Кроме того, не злоупотребляйте map где forEach будет иметь больше смысла. Тем не менее, это может быть просто let result = Set(array)
0

Самый простой способ - использовать 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)
  • 0
    Интересно, как эта производительность сравнивается с лучшими ответами здесь? Вы сравнивали?
0

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

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
    }
}
  • 0
    Это интересная демонстрация того , как contains(where:) могут быть реализованы с помощью только reduce , но и в производстве код , как это, это лучше всего использовать contains(where:) напрямую. С одной стороны, он может замкнуть поиск, если элемент найден на ранней стадии (нет необходимости продолжать поиск после этого, но этот метод reduce делает это в любом случае)
0

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

(используя 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]
  • 0
    почему -1 мой ответ ....
  • 0
    Это хороший первый подход, но он довольно сложный и многословный. Этот код делает слишком много вещей (дедупликация, усечение до 3 результатов), и нет простого способа повторно использовать его без массового копирования / вставки. Вы должны взглянуть на этот ответ . Затем вы могли бы написать что-то вроде scores.uniqued().prefix(3) . Просто!
Показать ещё 2 комментария
-2

Swift 3.0

extension Array where Element: Equatable {
    func withoutDuplicates() -> [Element] {
        return self.reduce([], { (objects, object) -> [Element] in
            var objects = objects
            if !objects.contains(object) {
                objects.append(object)
            }
            return objects
        })
    }
}
-2
  • Преобразовать в Set
  • Убедитесь, что количество элементов равно массиву до
  • Если нет, то создайте массив из Set

Ещё вопросы

Сообщество Overcoder
Наверх
Меню