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

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При работе с объектом ТаблицаЗначений в платформе 1С 7.7 разработчики регулярно сталкиваются с необходимостью отфильтровать данные и удалить строки, не соответствующие заданным условиям. Однако попытка удалить строку прямо внутри стандартного цикла выборки приводит к некорректному поведению программы или пропуску записей.

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

Анализ исходного кода и причин ошибки

Рассмотрим фрагмент кода, в котором предпринимается попытка очистки таблицы от ненужных данных:


ТзСортировкаПоКолву.ВыбратьСтроки();
Пока ТзСортировкаПоКолву.ПолучитьСтроку() = 1 Цикл    
    Если (ТзСортировкаПоКолву.Колво < От) И (ТзСортировкаПоКолву.Колво > До) Тогда 
        ТзСортировкаПоКолву.УдалитьСтроку();
    КонецЕсли;
КонецЦикла;

В приведенном коде допущены две критические ошибки:

  1. Нарушение работы внутреннего курсора выборки: Метод ВыбратьСтроки() в сочетании с ПолучитьСтроку() создает внутренний указатель на текущую запись. Вызов метода УдалитьСтроку() мгновенно смещает все последующие строки вверх на одну позицию. Из-за этого смещения курсор проскакивает следующую строку без проверки, а сам механизм выборки сбивается.
  2. Логическая ошибка в условии отбора: Условие (Колво < От) И (Колво > До) содержит логический оператор «И». Числовое значение не может быть одновременно строго меньше нижнего предела и больше верхнего. Для корректного отбора элементов за пределами заданного интервала следует использовать логическое «ИЛИ».

Рассмотрим несколько проверенных способов правильного удаления строк из таблицы значений.

Решение 1. Обратный цикл (от конца к началу)

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

Проанализируем реализацию обратного цикла с помощью Пока и метода ПолучитьСтрокуПоНомеру():


Индекс = ТзСортировкаПоКолву.КоличествоСтрок();
Пока Индекс > 0 Цикл
    ТзСортировкаПоКолву.ПолучитьСтрокуПоНомеру(Индекс);
    Если (ТзСортировкаПоКолву.Колво < От) Или (ТзСортировкаПоКолву.Колво > До) Тогда
        ТзСортировкаПоКолву.УдалитьСтроку();
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

Альтернативный вариант записи через цикл Для с отрицательным шагом:


Для Индекс = -ТзСортировкаПоКолву.КоличествоСтрок() По -1 Цикл
    ТзСортировкаПоКолву.ПолучитьСтрокуПоНомеру(-Индекс);
    Если (ТзСортировкаПоКолву.Колво < От) Или (ТзСортировкаПоКолву.Колво > До) Тогда
        ТзСортировкаПоКолву.УдалитьСтроку();
    КонецЕсли;
КонецЦикла;

Решение 2. Прямой цикл с условным приращением счетчика

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

Разберем пример такого алгоритма:


Индекс = 1;
Пока Индекс <= ТзСортировкаПоКолву.КоличествоСтрок() Цикл
    ТзСортировкаПоКолву.ПолучитьСтрокуПоНомеру(Индекс);
    Если (ТзСортировкаПоКолву.Колво < От) Или (ТзСортировкаПоКолву.Колво > До) Тогда
        ТзСортировкаПоКолву.УдалитьСтроку();
    Иначе
        Индекс = Индекс + 1;
    КонецЕсли;
КонецЦикла;

Решение 3. Перенос нужных строк в новую Таблицу Значений (для больших таблиц)

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

Для оптимизации скорости работы перенесем подходящие строки в новую копию таблицы:


// Выгружаем структуру исходной таблицы без строк
ТзСортировкаПоКолву.Выгрузить(ТзРезультат, 1, 0);

// Обходим исходную таблицу стандартным циклом без удаления
ТзСортировкаПоКолву.ВыбратьСтроки();
Пока ТзСортировкаПоКолву.ПолучитьСтроку() = 1 Цикл
    Если (ТзСортировкаПоКолву.Колво >= От) И (ТзСортировкаПоКолву.Колво <= До) Тогда
        ТзРезультат.НоваяСтрока();
        ТзРезультат.ЗаполнитьЗначения(ТзСортировкаПоКолву);
    КонецЕсли;
КонецЦикла;

// В ТзРезультат остаются только необходимые данные

Этот подход работает за один линейный проход и не расходует ресурсы на постоянные сдвиги памяти при удалении.

Оптимизация условий на уровне запроса

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

← На главную