Удаление дубликатов из массива в Swift: лучшие способы
Использование NSOrderedSet (простой способ)
Самый простой способ сохранить порядок — использовать NSOrderedSet из Foundation. Он автоматически удаляет дубликаты, сохраняя первый порядок вхождения:
import Foundation
let fruits = ["apple", "banana", "apple", "orange", "banana", "kiwi"]
let uniqueFruits = Array(NSOrderedSet(array: fruits)) as! [String]
print(uniqueFruits) // ["apple", "banana", "orange", "kiwi"]
Плюсы: коротко и читаемо.
Минусы: работает только с элементами, поддерживающими Hashable (строки, числа — подходят).
Расширение Array с Dictionary (Swift 5.0+)
Более производительный способ для больших массивов — использовать словарь для отслеживания уникальности:
extension Array where Element: Hashable {
func removingDuplicates() -> [Element] {
var seen = [Element: Bool]()
return filter { seen.updateValue(true, forKey: $0) == nil }
}
}
let uniqueFruits = fruits.removingDuplicates()
print(uniqueFruits) // ["apple", "banana", "orange", "kiwi"]
Метод updateValue(_:forKey:) возвращает nil, если ключа ещё не было — так мы оставляем только первое вхождение.
Сравнение производительности
NSOrderedSet— O(n) по времени, но требует приведения типов (cast).- Расширение с Dictionary — O(n), оптимально для больших массивов (1000+ элементов).
filter + contains— O(n²), избегайте для больших данных.
Универсальное решение для любых типов (не только Hashable)
Если элементы не поддерживают Hashable (например, кастомные структуры без Hashable), используйте замыкание для сравнения:
extension Array {
func removingDuplicates(by predicate: (Element, Element) -> Bool) -> [Element] {
var result: [Element] = []
for element in self {
if !result.contains(where: { predicate($0, element) }) {
result.append(element)
}
}
return result
}
}
// Пример для кастомных структур
struct Person {
let id: Int
let name: String
}
let people = [Person(id: 1, name: "Alice"), Person(id: 2, name: "Bob"), Person(id: 1, name: "Alice")]
let uniquePeople = people.removingDuplicates(by: { $0.id == $1.id })
print(uniquePeople.count) // 2
Внимание: этот метод — O(n²), используйте только для небольших массивов (до 100 элементов).
Рекомендация
Для 90% случаев используйте расширение с Dictionary — оно быстрое, сохраняет порядок и легко читается. Если массив маленький (до 50 элементов), можно использовать NSOrderedSet для краткости.
Альтернативный подход: использование OrderedSet из Collections (Swift 5.7+)
Начиная с Swift 5.7, в пакете Collections (от Apple) появился OrderedSet — структура данных, которая сочетает свойства Set и Array:
import Collections
let fruits = ["apple", "banana", "apple", "orange", "banana", "kiwi"]
let uniqueFruits = Array(OrderedSet(fruits))
print(uniqueFruits) // ["apple", "banana", "orange", "kiwi"]
Плюсы:
- Сохраняет порядок добавления.
- Работает быстро — O(1) для проверки уникальности.
- Не требует приведения типов (как NSOrderedSet).
Минусы: нужно добавить зависимость swift-collections в Package.swift или через SPM.
Этот вариант предпочтителен, если вы уже используете другие коллекции из этого пакета (например, Deque или Heap).
Ответов пока нет.
Войдите чтобы оставить ответ