Сохраняет ли partition by: исходный порядок элементов в каждой из двух частей?

Сохраняет ли partition(by:) исходный порядок элементов в каждой из двух частей?

Проходите собеседования с ИИ помощником Hintsage

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

Нет. partition(by:) гарантирует только разделение коллекции: элементы, для которых предикат вернул false, окажутся перед границей, а элементы с true — начиная с неё. Относительный порядок элементов внутри обеих частей не гарантируется.

Исторический контекст

Разбиение коллекции на две группы часто нужно для дальнейшей обработки без полной сортировки. Подход partition решает эту задачу преимущественно за счёт перестановки элементов на месте, избегая обязательного создания двух новых коллекций.

Цена такой эффективности — отсутствие стабильности. Если порядок элементов важен, простого разделения недостаточно.

Постановка проблемы

Предположим, нужно отделить подходящие элементы от остальных, сохранив исходный порядок. Использование partition(by:) может дать правильное содержимое обеих частей, но изменить порядок внутри них.

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

Подробное решение

Метод изменяет изменяемую коллекцию на месте и возвращает индекс границы. До этого индекса находятся элементы, для которых предикат дал false; начиная с него — элементы, для которых предикат дал true.

var values = [1, 4, 2, 5, 3] let boundary = values.partition { $0 >= 4 } assert(values[..<boundary].allSatisfy { $0 < 4 }) assert(values[boundary...].allSatisfy { $0 >= 4 })

После вызова нельзя делать вывод о порядке, например что числа в первой части останутся 1, 2, 3. Граница относится уже к изменённой коллекции, а не к исходным позициям элементов.

partition(by:) подходит, когда важны только две группы и эффективная перестановка элементов. Если порядок должен сохраниться, обычно применяют два последовательных filter, получая новые коллекции, либо реализуют стабильное разбиение отдельно; это требует дополнительной памяти или более сложного алгоритма.

Предикат следует делать без побочных эффектов. Логика не должна зависеть от порядка проверки элементов или от конкретного числа вызовов замыкания, поскольку основная гарантия метода касается результата разбиения, а не порядка работы алгоритма.

Ситуация из практики

В обработчике задач нужно разделить операции на готовые и ожидающие, сохранив порядок поступления внутри каждой группы. Рассматривались два варианта: partition(by:) — быстрее работает на месте и не требует двух результирующих массивов, но разрушает порядок; два вызова filter — сохраняют порядок и делают код очевидным, но создают новые массивы.

Для очереди задач выбран вариант с filter, потому что порядок поступления является частью бизнес-логики. Экономия на временной памяти не оправдывает риск выполнения задач в изменённой последовательности.

Что кандидаты часто упускают

  1. Возвращаемый индекс — это позиция в исходной коллекции или в изменённой?

    Это индекс границы в уже перестроенной коллекции. Он указывает на начало части, где предикат возвращает true; это не позиция элемента из исходного порядка.

  2. Можно ли использовать partition(by:), если нужно отсортировать элементы по условию?

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

  3. Почему результат может быть логически правильным, но практически непригодным?

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