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

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

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

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

Почему стандартный цикл «Для каждого» не подходит?

Разберем ситуацию изнутри. Когда мы используем конструкцию Для каждого Строка из ТаблицаЗначений, платформа 1С обращается к строкам по их внутреннему порядковому номеру. Представим, что нам нужно удалить 3-ю строку. Как только мы выполняем Удалить(Строка), бывшая 4-я строка моментально становится 3-й. Однако счетчик цикла уже переходит к следующему шагу и обращается к «новой» 4-й строке (которая раньше была 5-й). Таким образом, та строка, что сдвинулась на место удаленной, никогда не будет проверена. Это классическая ошибка, приводящая к некорректным результатам обработки данных.

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

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

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


ВсегоСтрок = ТаблицаЗначенийДляФорматирования.Количество();
Для Счетчик = 1 По ВсегоСтрок Цикл
    // Вычисляем индекс с конца
    ТекущийИндекс = ВсегоСтрок - Счетчик;
    СтрокаТЗ = ТаблицаЗначенийДляФорматирования[ТекущийИндекс];
    
    Если СтрокаТЗ.КоличествоУпаковок = 0 Тогда
        ТаблицаЗначенийДляФорматирования.Удалить(ТекущийИндекс);
    КонецЕсли;
КонецЦикла;

Важно: Обратите внимание, что индексы в 1С начинаются с 0, поэтому мы вычитаем счетчик из общего количества. Этот метод работает быстро и не требует выделения дополнительной памяти под вспомогательные объекты.

Способ 2. Использование вспомогательного массива

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

Разберем по шагам:

  1. Создаем пустой массив СтрокиДляУдаления.
  2. Проходим по таблице значений обычным циклом Для каждого.
  3. Если условие выполняется, добавляем текущую строку в массив.
  4. После завершения обхода запускаем цикл по массиву и удаляем строки из ТЗ.

Пример кода:


МассивСтрок = Новый Массив;
// Шаг 1: Поиск строк
Для каждого СтрокаТЗ из ТаблицаЗначенийДляФорматирования Цикл
    Если СтрокаТЗ.КоличествоУпаковок = 0 Тогда
        МассивСтрок.Добавить(СтрокаТЗ);
    КонецЕсли;
КонецЦикла;

// Шаг 2: Удаление найденных строк
Для каждого СтрокаИзМассива из МассивСтрок Цикл
    ТаблицаЗначенийДляФорматирования.Удалить(СтрокаИзМассива);
КонецЦикла;

Преимущество этого метода в том, что при удалении по прямой ссылке на объект (строку) нам не нужно беспокоиться об индексах. Ссылка остается валидной до момента удаления объекта.

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

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

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


Индекс = 0;
Пока Индекс < ТаблицаЗначенийДляФорматирования.Количество() Цикл
    СтрокаТЗ = ТаблицаЗначенийДляФорматирования[Индекс];
    Если СтрокаТЗ.КоличествоУпаковок = 0 Тогда
        ТаблицаЗначенийДляФорматирования.Удалить(Индекс);
        // Индекс не увеличиваем! На это место встала следующая строка.
    Иначе
        // Переходим к следующей строке, только если текущую не удаляли.
        Индекс = Индекс + 1;
    КонецЕсли;
КонецЦикла;

Этот метод очень эффективен, так как требует минимального количества операций и переменных.

Способ 4. Использование метода «НайтиСтроки»

Если ваше условие удаления — это простое равенство (например, КоличествоУпаковок = 0), можно воспользоваться встроенным методом НайтиСтроки(). Этот метод возвращает массив строк, который мы сразу можем использовать для удаления.


ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("КоличествоУпаковок", 0);

НайденныеСтроки = ТаблицаЗначенийДляФорматирования.НайтиСтроки(ПараметрыОтбора);
Для каждого СтрокаДляУдаления из НайденныеСтроки Цикл
    ТаблицаЗначенийДляФорматирования.Удалить(СтрокаДляУдаления);
КонецЦикла;

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

Способ 5. Фильтрация через копирование в новую таблицу

Иногда вместо удаления «плохих» строк проще и быстрее отобрать «хорошие» строки в новую таблицу. Это особенно актуально, если удаляемых строк очень много (например, более 80% таблицы).

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


// Копируем структуру колонок
НоваяТЗ = ТаблицаЗначенийДляФорматирования.СкопироватьКолонки();

Для каждого СтрокаТЗ из ТаблицаЗначенийДляФорматирования Цикл
    Если СтрокаТЗ.КоличествоУпаковок <> 0 Тогда
        ЗаполнитьЗначенияСвойств(НоваяТЗ.Добавить(), СтрокаТЗ);
    КонецЕсли;
КонецЦикла;

// Заменяем старую таблицу новой
ТаблицаЗначенийДляФорматирования = НоваяТЗ;

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

Профессиональный подход: Использование запроса

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

Рассмотрим алгоритм оптимизации:

  1. Выгружаем ТЗ во временную таблицу в запросе.
  2. Выполняем запрос с условием ГДЕ КоличествоУпаковок <> 0.
  3. Результат запроса выгружаем обратно в ТаблицаЗначений.

Важный нюанс: Если у вашей таблицы значений настроены индексы (объект Индексы), каждое удаление строки заставляет платформу пересчитывать их. При массовом удалении рекомендуется сначала удалить все индексы у ТЗ, произвести очистку, а затем создать их заново.

Заключение

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

← На главную