Удаление повторяющихся элементов из массива в Swift
у меня есть массив, который выглядит следующим образом:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]или, действительно, любая последовательность подобных типизированных частей данных. То, что я хочу сделать, это убедиться, что есть только один из каждого идентичного элемента. Например, приведенный выше массив станет:
[1, 4, 2, 6, 24, 15, 60] 30 ответов:
Вы можете свернуть свой собственный, например как этот (обновлено для 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 }
вы можете конвертировать в набор и обратно в массив снова довольно легко:
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: ) } if existingElements.count == 0 { results.append(element) } } return results } }который вы можете вызвать следующим образом:
let filteredElements = myElements.filterDuplicates { .PropertyOne == .PropertyOne && .PropertyTwo == .PropertyTwo }
Это берет некоторую хорошую информацию, которая уже находится на этой странице, и применяет подход Hashable/Set, когда это возможно, и возвращается к уравнительному коду в противном случае.
Swift 4 изменение для
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) } } } }
ограничение элементов коллекции равными вы можете использовать содержит:
extension Collection where Element: Equatable { var orderedSet: [Element] { var array: [Element] = [] return compactMap { if array.contains() { return nil } else { array.append() return } } } }
другой вариант-ограничение элемент коллекции Hashable и использовать для управления тем, какие элементы у вас в карте в результате:
extension Collection where Element: Hashable { var orderedSet: [Element] { var set = Set<Element>() return compactMap { set.insert().inserted ? : nil } } }С помощью Filter:
extension Collection where Element: Hashable { var orderedSet: [Element] { var set = Set<Element>() return filter { set.insert().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().inserted ? .append() : () } } }
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<Element>() return filter{ seen.insert().inserted } } }
любая попытка
insertтакже вернет кортеж:(inserted: Bool, memberAfterInsert: Set.Element). Смотрите документация.использование возвращенного значения помогает нам избежать зацикливания или выполнения любой другой операции.
альтернативное (если не оптимальное) решение от здесь использовать immutable типы, а не переменные:
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 }включено для контраста императивного подхода Жан-Пиллиппа с функциональным подходом.
в качестве бонуса эта функция работает со строками как массивами!
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
вот категория на
SequenceTypeкоторый сохраняет исходный порядок массива, но используетSetсделатьcontainsпоиск, чтобы избежатьO(n)стоимость массиваcontains(_:)метод.public extension Sequence where Iterator.Element: Hashable { public func unique() -> [Iterator.Element] { var buffer: [Iterator.Element] = [] var lookup = Set<Iterator.Element>() 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<Int>(myArray) myArray = Array(mySet) // [2, 4, 60, 6, 15, 24, 1]тогда вы можете заказать массив, как вы хотите
myArray.sort{ < } // [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<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будет исключить из конечного результата.
чуть более лаконичная синтаксическая версия ответ Даниэля Крома 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: ) == 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
для массивов, где элементы не являются ни Хэшируемыми, ни сопоставимыми (например, сложные объекты, словари или структуры), это расширение предоставляет обобщенный способ удаления дубликатов:
extension Array { func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element] { var uniqueKeys = Set<String>() return filter{uniqueKeys.insert("\(keyValue())").inserted} } } // example usage: (for a unique combination of attributes): peopleArray = peopleArray.filterDuplicate{ (.name, .age, .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 и сделал расширение массива, которое выполняет операции set-like над массивами, сохраняя при этом порядок элементов.
/// 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: ) == nil }) } func subtract(takeAway: [Element]) -> [Element] { let set = Set(takeAway) return self.filter({ !set.contains() }) } func intersect(with: [Element]) -> [Element] { let set = Set(with) return self.filter({ set.contains() }) } }
позвольте мне предложить ответ, похожий на ответ Скотта Гарднера но с более лаконичным синтаксисом с помощью reduce. Это решение удаляет дубликаты из массива пользовательских объектов (сохраняя начальный порядок)
// 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([]) { .contains() ? : + [] } 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)
Swift 4.2 Tested
extension Sequence where Iterator.Element: Hashable { func unique() -> [Iterator.Element] { var seen: [Iterator.Element: Bool] = [:] return self.filter { seen.updateValue(true, forKey: ) == nil } } }
вот ответ, который я придумал после поиска в интернете и не найдя то, что искал. Используя набор, Вы можете добавить все элементы уменьшить. Затем я беру результат и преобразую его в сортированный массив.
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]
здесь я сделал некоторые 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: , distinctAttribute: distinctAttribute()) }) 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 .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")
Я считаю, что было бы хорошо предложить
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"]
Swift 3
на основе ответ Жан-Филиппа Пелле, я обновил его синтаксис для 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]
в 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 } }
Я сделал простое расширение для этой цели.
extension Array where Element: Equatable { func containsHowMany(_ elem: Element) -> Int { return reduce(0) { == elem ? + 1 : } } func duplicatesRemoved() -> Array { return self.filter { self.containsHowMany() == 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.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]()) { .contains() ? : + [] } } }использование:
["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()или
["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
сохранить уникальные значения и сохранить сортировка в массиве.
(используя 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]
Comments