Какой цикл лучше использовать вместо for при изменении индексов внутри цикла?
Что проверяют
Понимаете ли вы, почему удаление или вставка элементов во время обхода ломает счётчик, и умеете ли выбрать конструкцию под задачу. Следом обычно просят написать удаление элементов по условию и оценить сложность.
Как отвечать
Сначала назовите проблему: arr.splice(i, 1) сдвигает все следующие элементы влево, а i++ перескакивает через тот, что встал на место удалённого. Отсюда пропуски и выход за границы.
Дальше варианты по порядку предпочтения:
filterбез мутации:const rest = arr.filter(x => !isBad(x)). Один проход, O(n), исходный массив не тронут. В состоянии React мутировать массив нельзя, так что там это основной путь;whileс ручным шагом: индекс растёт только тогда, когда элемент оставлен, а после удаления остаётся на месте. Это и есть ответ на вопрос «какой цикл вместоfor», когда шаг зависит от условия;- обратный
for:for (let i = arr.length - 1; i >= 0; i--). Удаление сдвигает только уже пройденную часть, пропусков нет; - два указателя: если нужна мутация на месте без квадратичной сложности от
splice, переписывайте оставляемые элементы по индексу записи и в конце обрежьте массив черезarr.length = write.
Уточните, что splice внутри цикла даёт O(n²): каждое удаление сдвигает хвост массива, и на сотне тысяч элементов это уже заметно.
Где спотыкаются
Советуют forEach или for...of, считая, что они «сами разберутся с индексами». Оба идут по живому массиву по внутреннему индексу и при splice точно так же пропускают элементы.
Забывают, что из forEach нельзя выйти через break, а return лишь завершает текущий вызов колбэка. Если нужен досрочный выход, берите for...of, some или find.
Мутируют массив из состояния компонента на месте. Ссылка не меняется, React и мемоизированные селекторы не видят изменений, и компонент не перерисуется.