Как правильно удалять элементы из массива в 1С при выполнении условия

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с массивами в 1С начинающие разработчики часто сталкиваются с проблемой: при попытке удалить элемент внутри цикла программа либо выдает ошибку "Индекс находится за границами массива", либо пропускает следующие за удаленным элементы. Это происходит из-за того, что массив в 1С — это динамическая коллекция, и при удалении одного объекта все последующие "сдвигаются" на одну позицию влево, изменяя свои индексы.

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

Почему нельзя использовать цикл «Для каждого» для удаления

Рассмотрим ситуацию, которую описал автор темы. При использовании конструкции Для каждого ЭлементМассива Из Массив Цикл система инициализирует внутренний счетчик (итератор). Если мы удаляем ЭлементМассива, структура коллекции меняется мгновенно. Элемент, который был следующим, занимает место удаленного. Однако итератор цикла на следующей итерации все равно переходит к следующему порядковому номеру, фактически перепрыгивая через один элемент.

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

Способ 1. Удаление элементов в обратном порядке (самый эффективный)

Это «золотой стандарт» разработки в 1С. Суть метода заключается в том, чтобы начать обход массива не с начала, а с конца. При удалении элемента в конце или середине массива индексы элементов, которые мы еще не обработали (находящиеся перед удаляемым), остаются неизменными. Таким образом, мы никогда не выйдем за границы массива и не пропустим данные.

Разберем по шагам, как реализовать этот алгоритм:

  1. Определяем верхнюю границу массива с помощью функции ВГраница().
  2. Запускаем цикл Для с шагом -1.
  3. Проверяем условие и удаляем элемент по текущему индексу.

// Создаем исходный массив
Массив = Новый Массив;
Для Счетчик = 0 По 30 Цикл
    Массив.Добавить(Счетчик);
КонецЦикла;

// Удаляем элементы, кратные 3, в обратном порядке
Для Индекс = -(Массив.ВГраница()) По 0 Цикл
    ТекущийИндекс = -Индекс;
    Элемент = Массив[ТекущийИндекс];
    
    Если Элемент % 3 = 0 Тогда
        Массив.Удалить(ТекущийИндекс);
    КонецЕсли;
КонецЦикла;

Обратите внимание на использование функции ВГраница(). Она возвращает индекс последнего элемента (например, если в массиве 31 элемент, ВГраница() вернет 30). Использование Количество() в циклах Для часто приводит к ошибке «вне диапазона», так как индексы начинаются с нуля.

Способ 2. Использование вспомогательного массива (метод «Пометки»)

Если логика удаления сложная или вы опасаетесь запутаться в индексах обратного цикла, можно воспользоваться методом двух проходов. Сначала мы определим все элементы, которые подлежат удалению, и сохраним их в отдельную коллекцию, а затем вторым циклом удалим их из основного массива.

Посмотрим на пример реализации:


// Массив для сбора элементов к удалению
ЭлементыДляУдаления = Новый Массив;

// Первый проход: ищем подходящие значения
Для Каждого Элемент Из Массив Цикл
    Если Элемент % 3 = 0 Тогда
        ЭлементыДляУдаления.Добавить(Элемент);
    КонецЕсли;
КонецЦикла;

// Второй проход: удаляем найденные значения
Для Каждого ЭлементКБрак Из ЭлементыДляУдаления Цикл
    Массив.Удалить(Массив.Найти(ЭлементКБрак));
КонецЦикла;

Важный нюанс: Этот способ хорошо работает с уникальными объектами (например, ссылками на документы или справочники). Если же в массиве хранятся простые числа, которые могут повторяться, метод Массив.Найти() всегда будет возвращать индекс первого найденного вхождения, что может привести к неверному результату.

Способ 3. Цикл «Пока» с ручным управлением индексом

Этот метод позволяет обходить массив с начала (с нулевого индекса), но дает нам контроль над тем, когда переходить к следующему элементу. Мы увеличиваем индекс только в том случае, если элемент не был удален.

Проанализируем код:


Индекс = 0;
Пока Индекс <= Массив.ВГраница() Цикл
    Элемент = Массив[Индекс];
    
    Если Элемент % 3 = 0 Тогда
        // Удаляем элемент, индекс следующего автоматически станет текущим
        Массив.Удалить(Индекс);
    Иначе
        // Переходим к следующему элементу только если текущий остался на месте
        Индекс = Индекс + 1;
    КонецЕсли;
КонецЦикла;

Этот способ интуитивно понятен и исключает риск пропуска элементов, так как мы «стоим на месте» до тех пор, пока текущий элемент не пройдет проверку условием.

Оптимизация: Инверсия логики (создание нового массива)

В профессиональной разработке часто вместо удаления лишнего используют копирование нужного. Если массив очень большой (десятки тысяч элементов), операция Удалить() в цикле будет работать медленно, так как 1С вынуждена перестраивать память при каждом удалении.

Рассмотрим, как сделать это быстрее:


НовыйМассив = Новый Массив;

Для Каждого Элемент Из Массив Цикл
    // Добавляем только те элементы, которые НЕ кратны 3
    Если Не (Элемент % 3 = 0) Тогда
        НовыйМассив.Добавить(Элемент);
    КонецЕсли;
КонецЦикла;

// Заменяем старый массив новым результатом
Массив = НовыйМассив;

Этот метод работает за один проход и является наиболее производительным, так как операция Добавить() выполняется быстрее, чем Удалить() из середины коллекции.

Резюме и рекомендации

Подведем итоги нашего разбора:

Применяя эти подходы, вы забудете об ошибке "Индекс находится за границами массива" и сделаете свой код более надежным и понятным для коллег.

← На главную