Нужно выбрать элементы, сохранив их взаимный порядок. Что будет напечатано и сохраняет ли filter порядок исходной коллекции?
let values = [("A", 2), ("B", 1), ("C", 2), ("D", 1)]
let selected = values.filter { $0.1 == 2 }
print(selected.map { $0.0 })
Будет напечатано ["A", "C"]. filter сохраняет относительный порядок элементов, прошедших проверку: он не сортирует результат и не переставляет подходящие элементы.
Операции высшего порядка вроде filter позволяют описывать преобразование коллекции через намерение: какие элементы оставить, а не через ручное управление индексами и вставками. Такой подход уменьшает количество изменяемого состояния и делает сохранение порядка явным свойством операции.
Для последовательной обработки данных это особенно важно: отбор обычно должен удалять неподходящие элементы, не меняя порядок оставшихся.
Если после фильтрации ожидается исходный порядок, нельзя незаметно заменять filter сортировкой или использовать алгоритм, который переставляет элементы ради оптимизации. Ошибка приведёт к изменению приоритета, хронологии или порядка отображения данных.
В приведённом коде элементы A и C проходят предикат. Поскольку A находится перед C во входной коллекции, результат сохраняет именно эту последовательность.
filter последовательно обходит исходную последовательность, передаёт каждый элемент в предикат и добавляет элемент в результат только при значении true. Относительный порядок добавления совпадает с порядком обхода исходной последовательности.
Результат работы для массива — новый массив; исходный values не изменяется. В примере алгоритм эквивалентен следующей логике:
Для конечной последовательности предикат проверяется по всем элементам, потому что filter должен определить, какие элементы оставить. Это отличается от first(where:) или contains(where:), которые могут остановиться после нахождения подходящего элемента.
Обычно стоимость фильтрации составляет O(n) по времени и O(k) дополнительной памяти для k выбранных элементов. filter не обязан сохранять индексы исходной коллекции: результат содержит элементы в новом компактном представлении.
В ленте событий нужно скрыть элементы определённого типа, не нарушив хронологию. Возможны два подхода: вручную собирать результат с индексами или вызвать filter.
Ручной обход даёт полный контроль, но увеличивает объём кода и риск ошибки при изменении результата во время обхода. filter проще, явно выражает намерение и сохраняет порядок, поэтому выбранным решением будет он.
Например, после фильтрации событий можно безопасно отображать результат в том же порядке, в котором события пришли. Если же требуется именно новый порядок, фильтрацию следует явно отделить от sorted, чтобы не смешивать отбор и сортировку.
filter исходный массив?Нет. filter возвращает новый результат и не изменяет переменную исходного массива. Если требуется изменить массив на месте, результат нужно присвоить обратно, например values = values.filter { ... }, при условии что values объявлен через var.
filter досрочно завершить обход после первого совпадения?Нет, это не его назначение. Он должен найти все подходящие элементы, поэтому для обычной конечной последовательности проверяет каждый элемент. Для поиска одного элемента следует использовать first(where:), а для проверки существования — contains(where:).
Они сохранятся. filter не удаляет дубликаты и не применяет правила уникальности: каждый элемент, для которого предикат вернул true, попадает в результат в исходной позиции относительно других оставленных элементов. Для уникализации нужен отдельный механизм, например Set, но он может изменить порядок и предъявляет требования к Hashable.