Как отобрать и оставить в таблице значений строки с несколькими условиями или списком значений?

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

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

Многие разработчики пробуют решить задачу с помощью стандартных методов Скопировать() или НайтиСтроки(), передавая в них структуру отбора. Однако конструкция вида Новый Структура("Количество", 1 и 2) приводит к синтаксической ошибке. Выясним причину такого поведения: методы Скопировать() и НайтиСтроки() при передаче структуры отбора поддерживают исключительно проверку на точное равенство. В структуре невозможно задать логическое «ИЛИ», диапазоны сравнения или список значений для одного поля — все переданные ключи структуры всегда объединяются платформой только по условию «И».

Разберем основные и наиболее эффективные способы решения этой задачи.

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

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

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


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

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

Способ 2. Предварительный сбор строк в массив с последующим удалением

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

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


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

Для Каждого СтрТЗ Из тз_Осн Цикл
    Если Не (СтрТЗ.Количество = 1 Или СтрТЗ.Количество = 2) Тогда
        СтрокиКУдалению.Добавить(СтрТЗ);
    КонецЕсли;
КонецЦикла;

Для Каждого СтрУдалить Из СтрокиКУдалению Цикл
    тз_Осн.Удалить(СтрУдалить);
КонецЦикла;

Этот способ удобен тем, что в метод Удалить() можно передавать не только числовой индекс строки, но и саму ссылку на объект СтрокаТаблицыЗначений.

Способ 3. Использование объекта ПостроительЗапроса

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

Разберем по шагам, как настроить отбор по списку значений:

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

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


СписокКоличеств = Новый Массив;
СписокКоличеств.Добавить(1);
СписокКоличеств.Добавить(2);

Построитель = Новый ПостроительЗапроса;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(тз_Осн);

ЭлементОтбора = Построитель.Отбор.Добавить("Количество");
ЭлементОтбора.Использование = Истина;
ЭлементОтбора.ВидСравнения = ВидСравнения.ВСписке;
ЭлементОтбора.Значение = СписокКоличеств;

тзРез = Построитель.Результат.Выгрузить();

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

Способ 4. Формирование новой таблицы через СкопироватьКолонки()

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

В высоконагруженных сценариях гораздо быстрее создать пустую таблицу с аналогичной структурой колонок и перенести в нее только нужные строки:


тзРез = тз_Осн.СкопироватьКолонки();

Для Каждого СтрИсходная Из тз_Осн Цикл
    Если СтрИсходная.Количество = 1 Или СтрИсходная.Количество = 2 Тогда
        ЗаполнитьЗначенияСвойств(тзРез.Добавить(), СтрИсходная);
    КонецЕсли;
КонецЦикла;

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

Способ 5. Использование временных таблиц в запросе

Таблицу значений можно передать параметром в обычный объект Запрос, поместить ее во временную таблицу и применить стандартную секцию ГДЕ языка запросов 1С.

Однако здесь существует строгое ограничение: чтобы таблица значений могла быть помещена во временную таблицу (конструкция ПОМЕСТИТЬ ВтТаблица), все используемые в запросе колонки обязательно должны иметь явно заданное описание типов (объект ОписаниеТипов). Если тип колонки не определен или составной без явных ограничений, платформа вернет ошибку на этапе выполнения запроса к СУБД.

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

← На главную