Как правильно программно удалять строки из табличной части в 1С?

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

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

В чем причина ошибки пропуска строк?

Проанализируем стандартный ошибочный пример, с которого часто начинаются подобные трудности:


Для Каждого Строка ИЗ Объект.ТабличнаяЧасть Цикл
    Если УсловиеВыполнено Тогда
        Объект.ТабличнаяЧасть.Удалить(Строка);
    КонецЕсли;
КонецЦикла;

В этом сценарии итератор цикла переходит к следующему элементу по порядковому номеру (индексу). Предположим, у нас есть строки с индексами 0, 1, 2 и 3. На первом шаге удаляется строка с индексом 0. В момент вызова метода Удалить() коллекция немедленно сдвигается: строка, которая была на позиции 1, теперь занимает позицию 0, а бывшая вторая строка становится первой. На следующей итерации цикл переходит к индексу 1, в результате чего строка, переместившаяся на позицию 0, оказывается полностью пропущенной.

Чтобы избежать подобных коллизий со смещением индексов, рассмотрим несколько надежных подходов к решению этой задачи.

Способ 1. Предварительный сбор строк в массив (Рекомендуемый)

Наиболее наглядный, безопасный и универсальный способ — сначала отобрать все строки, подлежащие удалению, в отдельный Массив, а затем удалить их отдельным циклом. В таком случае обход исходной коллекции не нарушается, так как во время первого цикла модификация табличной части не происходит.

Разберем по шагам реализацию данного подхода:

  1. Создаем новый массив для хранения ссылок на удаляемые строки.
  2. Проходим циклом Для Каждого по табличной части и складываем подходящие строки в массив.
  3. Обходим полученный массив и удаляем накопленные строки из табличной части.

Посмотрим на пример программного кода:


МассивДляУдаления = Новый Массив;

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

// Второй проход: непосредственное удаление
Для Каждого СтрокаТЧ Из МассивДляУдаления Цикл
    Объект.ТабличнаяЧасть.Удалить(СтрокаТЧ);
КонецЦикла;

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

Способ 2. Обход коллекции с конца (обратный цикл)

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

Обратим внимание на важную особенность платформы 1С:Предприятие: конструкция цикла Для ... По ... Цикл умеет изменять шаг счетчика только на +1. Написать обратный шаг напрямую в стандартном цикле Для нельзя. Поэтому обход с конца выполняется с помощью цикла Пока.

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


Индекс = Объект.ТабличнаяЧасть.Количество() - 1;

Пока Индекс >= 0 Цикл
    СтрокаТЧ = Объект.ТабличнаяЧасть[Индекс];
    
    Если СтрокаТЧ.Количество = 0 Тогда
        Объект.ТабличнаяЧасть.Удалить(Индекс);
    КонецЕсли;
    
    Индекс = Индекс - 1;
КонецЦикла;

В этом варианте метод Удалить() принимает параметром сам Индекс строки. Этот способ не требует выделения дополнительной памяти под вспомогательный массив.

Способ 3. Прямой обход с контролем шага

Если по каким-то причинам требуется идти от первой строки к последней, но без создания массива, можно использовать цикл Пока с условным увеличением индекса. Главное правило: увеличивать индекс нужно только тогда, когда строка не была удалена. Если строка удаляется, следующая за ней строка автоматически занимает текущий индекс, и смещать счетчик не требуется.

Разберем этот вариант на примере:


Индекс = 0;

Пока Индекс < Объект.ТабличнаяЧасть.Количество() Цикл
    СтрокаТЧ = Объект.ТабличнаяЧасть[Индекс];
    
    Если СтрокаТЧ.Количество = 0 Тогда
        Объект.ТабличнаяЧасть.Удалить(Индекс);
    Иначе
        Индекс = Индекс + 1;
    КонецЕсли;
КонецЦикла;

Способ 4. Удаление интерактивно выделенных строк

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

Рассмотрим, как правильно обработать удаление выделенных строк:


Для Каждого Идентификатор Из Элементы.ТабличнаяЧасть.ВыделенныеСтроки Цикл
    Строка = Объект.ТабличнаяЧасть.НайтиПоИдентификатору(Идентификатор);
    
    Если Строка <> Неопределено И УсловиеВыполнено Тогда
        Объект.ТабличнаяЧасть.Удалить(Строка);
    КонецЕсли;
КонецЦикла;

Оптимизация при работе с большими объемами данных

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

Для ускорения обработки сотен и тысяч строк рекомендуется инвертировать логику: скопировать или отобрать строки, которые должны остаться, затем очистить коллекцию методом Очистить() и восстановить нужные данные методом Загрузить(). Это сократит время выполнения операции в разы.

← На главную