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

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

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

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

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

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

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

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

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

     let unique = Array(Set(originals)) 

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

    Многие ответы доступны здесь, но я пропустил это простое расширение, подходящее для 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 } 

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

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

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

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

    Swift 3.0

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

    Swift 4

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

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

    Ограничение элементов коллекции 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() return compactMap { set.insert($0).inserted ? $0 : nil } } } 

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

     extension Collection where Element: Hashable { var orderedSet: [Element] { var set = Set() 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 = [] 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] 

    Swift 4

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

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

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

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

     func deleteDuplicates(seq:S)-> S { let s = reduce(seq, S()){ ac, x in contains(ac,x) ? ac : ac + [x] } return s } 

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

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

    быстрый 2

    с ответом функции uniq :

     func uniq(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 

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

     public extension Sequence where Iterator.Element: Hashable { public func unique() -> [Iterator.Element] { var buffer: [Iterator.Element] = [] var lookup = Set() for element in self { guard !lookup.contains(element) else { continue } buffer.append(element) lookup.insert(element) } return buffer } } 

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

     public extension Sequence where Iterator.Element: Equatable { public func unique() -> [Iterator.Element] { var buffer: [Iterator.Element] = [] for element in self { guard !buffer.contains(element) else { continue } buffer.append(element) } return buffer } } 

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

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

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

    Затем вы можете заказать свой массив, как хотите

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

    Еще одно решение 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(array: Array) -> Array { var unique = Set() 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() 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 исключает его из окончательного результата.

    Немного более сжатая синтаксическая версия ответа Дайнала Кром «Swift 2» с использованием закрывающего закрытия и сокращенного имени аргумента, который, по-видимому, основан на исходном ответе Airspeed Velocity :

     func uniq(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 type (или метод, который возвращает Equatable type, например 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 

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

     extension Array { func filterDuplicate(_ keyValue:(Element)->T) -> [Element] { var uniqueKeys = Set() 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 и позволяет использовать разные комбинации полей для уникальности.

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

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

    ,

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

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

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

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

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

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

     let initialArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6] let distinct2 = initialArray.reduce(Set(), combine: { (set, current) -> Set 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] 

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

     struct DistinctWrapper : Hashable { var underlyingObject: T var distinctAttribute: String var hashValue: Int { return distinctAttribute.hashValue } } func distinct(source: S, distinctAttribute: (T) -> String, resolution: (T, T) -> T) -> [T] { let wrappers: [DistinctWrapper] = source.map({ return DistinctWrapper(underlyingObject: $0, distinctAttribute: distinctAttribute($0)) }) var added = Set>() 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 == (lhs: DistinctWrapper, rhs: DistinctWrapper) -> 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") 

    Я считаю, что было бы неплохо предложить функцию 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 ), поскольку вы не можете мутировать константу Array (т.е. 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"] 

    Swift 3

    Основываясь на ответе Жана-Филиппа Пелле , я обновил его синтаксис для Swift 3.

     func uniq(source: S) -> [T] { var buffer = [T]() var added = Set() 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] 

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

     extension Array where Element:Hashable { var unique: [Element] { var set = Set() //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 } } 

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

     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] 

    Это также работает (Swift 4)

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

    Swift 4.x:

     extension Sequence where Iterator.Element: Hashable { func unique() -> [Iterator.Element] { return Array(Set(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() 

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

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

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

      var top3score: [Int] = [] outerLoop: for i in 0.. 
    Давайте будем гением компьютера.