> Как реализовать фильтрацию массива с сохранением уникальных элементов и порядка (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: 2GIS

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Для фильтрации массива с сохранением уникальных элементов и порядка в Swift используйте NSOrderedSet для простых случаев или комбинацию Set и filter для контроля над логикой. Основной подход - проходим по массиву, добавляем элементы в Set для отслеживания дубликатов и оставляем только те, которые встречаются впервые. Это гарантирует сохранение исходного порядка и O(n) сложность.

Подробное объяснение

Задача сводится к удалению дубликатов без изменения порядка элементов. Ключевая идея - использовать вспомогательную структуру данных для быстрой проверки уникальности. Set в Swift обеспечивает константное время доступа, поэтому алгоритм работает за O(n).

Основные подходы:

  1. NSOrderedSet - готовое решение из Foundation, сохраняет порядок и уникальность. Минус - требует преобразования типов и работает только с Hashable элементами.

  2. Ручная реализация с Set - более гибкий вариант. Создаём пустой Set, затем фильтруем массив, проверяя, был ли элемент уже добавлен. Если нет - добавляем и оставляем в результате.

  3. reduce с кортежем - функциональный стиль, но менее читаемый и менее эффективный из-за создания промежуточных структур.

Для элементов, не соответствующих Hashable, можно использовать NSObject или кастомную логику сравнения через Equatable с ручным поиском (O(n²) - не рекомендуется для больших массивов).

На практике

В реальных проектах чаще всего используется вариант с Set, так как он:

  • не требует импорта Foundation (если не нужен NSOrderedSet);
  • работает с любыми Hashable типами;
  • легко расширяется (например, можно добавить условие фильтрации по свойству объекта).

Для массивов структур или классов с идентификаторами удобно фильтровать по id, а не по всему объекту - это ускоряет проверку уникальности.

Пример кода

SWIFT
// Базовый вариант для Hashable элементов
extension Array where Element: Hashable {
func unique() -> [Element] {
var seen = Set<Element>()
return filter { element in
if seen.contains(element) {
return false
} else {
seen.insert(element)
return true
}
}
}
}
let numbers = [1, 2, 3, 2, 4, 1, 5]
print(numbers.unique()) // [1, 2, 3, 4, 5]
// Вариант с фильтрацией по свойству
struct User {
let id: Int
let name: String
}
let users = [
User(id: 1, name: "Alice"),
User(id: 2, name: "Bob"),
User(id: 1, name: "Alice Again")
]
var seenIDs = Set<Int>()
let uniqueUsers = users.filter { user in
guard !seenIDs.contains(user.id) else { return false }
seenIDs.insert(user.id)
return true
}
print(uniqueUsers.map { $0.name }) // ["Alice", "Bob"]

Как отвечать на собеседовании

Начните с простого решения через Set, объясните сложность O(n). Затем упомяните альтернативы: NSOrderedSet для быстрого прототипирования и reduce для функционального стиля. Подчеркните важность сохранения порядка - это ключевое отличие от простого использования Set для дедупликации. Если спросят про не-Hashable элементы, предложите вариант с Equatable и объясните trade-off по производительности.

Что проверяет интервьюер

  • Понимание структур данных и их временной сложности.
  • Умение работать с дженериками и расширениями в Swift.
  • Способность учитывать edge cases: пустой массив, все элементы уникальны, все дубликаты.
  • Знание альтернативных подходов и их ограничений.

Типичные ошибки

  • Использование Set(array) напрямую - теряется порядок.
  • Забывают про Hashable ограничение и пытаются применить к произвольным типам.
  • Реализация через contains внутри filter - даёт O(n²) и падает на больших данных.
  • Не учитывают, что NSOrderedSet требует импорта Foundation и работает медленнее на больших массивах из-за мостов с Objective-C.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью