К статье
Практический материал

Разработка и Конфигурация · 9 мин

Как в цикле удалить строки из табличной части 1С

Как правильно удалять строки табличной части 1С в цикле: обратный обход, метод Удалить(), типичные ошибки с индексами и безопасные приёмы с примерами кода.

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

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

Почему прямой обход с удалением ломает логику

Табличная часть в 1С — это упорядоченная коллекция строк с индексами от 0 до Количество() − 1. Когда вы удаляете строку с индексом 2, строка с индексом 3 становится строкой с индексом 2, и вся коллекция «сжимается». Если цикл идёт вперёд, следующая итерация обратится к индексу 3 — а это уже бывшая строка 4. Одна строка оказывается пропущенной без всякого сообщения об ошибке.

Ещё опаснее ситуация, когда удаление выполняется внутри цикла «Для Каждого Строка Из ТабличнаяЧасть Цикл». Платформа в этом случае может повести себя непредсказуемо: часть строк пропускается, а при удалении последней строки возможно обращение к несуществующему элементу. Такой код может «работать» на тестовых данных с одной строкой и падать или давать неверный результат на реальных объёмах.

Обратный цикл — универсальный способ удаления по условию

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

ТабЧасть = Объект.Товары;

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

Пока Индекс >= 0 Цикл

Строка = ТабЧасть[Индекс];

Если Строка.Количество = 0 Тогда

ТабЧасть.Удалить(Индекс);

КонецЕсли;

Индекс = Индекс - 1;

КонецЦикла;

Тот же приём работает и через метод Удалить() с передачей самой строки, а не индекса: ТабЧасть.Удалить(Строка). Но внутри обратного цикла удобнее оперировать индексом — так код короче и нагляднее. Условие внутри цикла может быть любым: проверка количества, суммы, признака пометки, соответствия отбору по номенклатуре.

Удаление через индексный цикл «Для» с шагом -1

Альтернативная форма записи — цикл «Для» с отрицательным шагом. Он компактнее и привычнее тем, кто пришёл из других языков:

ТабЧасть = Объект.Товары;

Для Индекс = -ТабЧасть.Количество() + 1 По 0 Цикл

Строка = ТабЧасть[-Индекс];

Если Строка.Пометка Тогда

ТабЧасть.Удалить(-Индекс);

КонецЕсли;

КонецЦикла;

Обратите внимание на приём с отрицательным индексом: встроенный язык 1С не поддерживает «Для Индекс = N По 0 Цикл Шаг -1» в явном виде, поэтому используется перебор отрицательных значений. Оба варианта — и «Пока», и «Для» — дают одинаковый результат, выбирайте по читаемости в контексте вашего кода.

Когда цикл не нужен: Очистить() и другие приёмы

Если требуется удалить все строки табличной части, цикл вообще не нужен — используйте метод Очистить():

Объект.Товары.Очистить();

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

СтрокиКУдалению = Новый Массив;

Для Каждого Строка Из Объект.Товары Цикл

Если Строка.Отменено Тогда

СтрокиКУдалению.Добавить(Строка);

КонецЕсли;

КонецЦикла;

Для Каждого Строка Из СтрокиКУдалению Цикл

Объект.Товары.Удалить(Строка);

КонецЦикла;

Для таблицы значений (не табличной части объекта) есть ещё вариант через НайтиСтроки() с последующим удалением найденных строк, либо через отбор и построение новой таблицы. НайтиСтроки возвращает массив строк, и удалять их можно точно так же отдельным циклом.

Сравнение способов удаления

Обратный цикл (Пока/Для)

Универсален для любых условий, минимальный расход памяти. Требует аккуратности с индексами.

Сбор в массив + удаление

Самый читаемый и безопасный вариант, обход и удаление разнесены. Создаёт дополнительный массив в памяти.

Очистить()

Только для удаления всех строк сразу. Условия задать нельзя.

НайтиСтроки + удаление

Подходит для таблиц значений с отбором по равенству. Сложные условия с «ИЛИ» выразить трудно.

Особенности на форме и в модуле объекта

На управляемой форме табличная часть доступна через Объект.ИмяТабличнойЧасти, и все описанные приёмы работают без изменений. После удаления строк форма обновит отображение автоматически, вызывать обновление вручную обычно не требуется. Если удаление выполняется в обработчике команды формы, помните про директивы &НаКлиенте и &НаСервере: коллекция доступна в обоих контекстах, но серверные вызовы внутри цикла заметно замедлят работу.

В модуле объекта (например, в ОбработкаПроведения или ПередЗаписью) обращайтесь к табличной части напрямую по имени: Товары.Удалить(Индекс). Здесь

Как проверить корректность удаления

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

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

Контрольные точки

Как проверить результат

Количество строк после удаления
Норма

Равно исходному минус число строк, подходящих под условие

Тревожный признак

Удалено меньше или больше, чем попадает под условие

Граничные строки
Норма

Первая и последняя строки обрабатываются корректно

Тревожный признак

Крайние строки пропускаются или удаляются ошибочно

Данные после записи
Норма

После перезаписи объекта состав строк сохраняется

Тревожный признак

Удалённые строки возвращаются после записи

Частые вопросы

Можно ли удалять строки в цикле «Для Каждого», если сразу сделать Прервать?

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

Чем отличается Удалить() от Очистить()?

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

Работают ли эти приёмы с динамическими списками на форме?

Нет. Динамический список — это результат запроса, он доступен только для чтения. Удалять строки нужно в самих объектах (документах, справочниках), после чего список обновится автоматически.

Как удалить строки табличной части у объекта, не открывая форму?

Получите объект методом ПолучитьОбъект() у ссылки, выполните обратный цикл с удалением или Очистить(), затем вызовите Записать(). Без вызова Записать() изменения не сохранятся в базе данных.

Почему после удаления строки в цикле возникает ошибка «Индекс находится за границами массива»?

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

Итог

Что важно запомнить

Удаляйте строки табличной части обратным циклом от конца к началу или собирайте строки в массив перед удалением. Для полной очистки используйте Очистить(). Не передавайте НомерСтроки в Удалить() вместо индекса и проверяйте результат на граничных строках.