При разработке в среде 1С часто возникает задача отфильтровать строки в объекте ТаблицаЗначений. Например, требуется оставить только строки, где значение колонки равно 1 или 2, а все остальные записи удалить.
Многие разработчики пробуют решить задачу с помощью стандартных методов Скопировать() или НайтиСтроки(), передавая в них структуру отбора. Однако конструкция вида Новый Структура("Количество", 1 и 2) приводит к синтаксической ошибке. Выясним причину такого поведения: методы Скопировать() и НайтиСтроки() при передаче структуры отбора поддерживают исключительно проверку на точное равенство. В структуре невозможно задать логическое «ИЛИ», диапазоны сравнения или список значений для одного поля — все переданные ключи структуры всегда объединяются платформой только по условию «И».
Разберем основные и наиболее эффективные способы решения этой задачи.
Посмотрим на самый распространенный и простой вариант — удаление лишних строк непосредственно из исходной таблицы значений. Если мы попытаемся удалять строки прямым обходом от нулевого индекса к концу, то при удалении текущей строки все последующие строки сдвинутся вверх, индексы сместятся, и часть данных будет пропущена.
Чтобы избежать смещения индексов, организуем обход коллекции в обратном направлении — от максимального индекса к нулю. Проанализируем пример кода:
Индекс = тз_Осн.Количество() - 1;
Пока Индекс >= 0 Цикл
ТекущаяСтрока = тз_Осн[Индекс];
Если Не (ТекущаяСтрока.Количество = 1 Или ТекущаяСтрока.Количество = 2) Тогда
тз_Осн.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
В этом примере мы проверяем обратное условие: если количество не равно 1 и не равно 2, то вызываем метод Удалить(), передавая индекс текущей строки. Так как обход идет с конца таблицы, удаление текущего элемента никак не влияет на номера еще не обработанных строк.
Рассмотрим еще один безопасный вариант циклического удаления. Чтобы не путаться с индексами и шагами цикла, разделим операцию на два последовательных этапа: сначала найдем все неподходящие строки и сохраним их ссылки в объект Массив, а затем во втором цикле удалим их из таблицы.
Посмотрим на программную реализацию:
СтрокиКУдалению = Новый Массив;
Для Каждого СтрТЗ Из тз_Осн Цикл
Если Не (СтрТЗ.Количество = 1 Или СтрТЗ.Количество = 2) Тогда
СтрокиКУдалению.Добавить(СтрТЗ);
КонецЕсли;
КонецЦикла;
Для Каждого СтрУдалить Из СтрокиКУдалению Цикл
тз_Осн.Удалить(СтрУдалить);
КонецЦикла;
Этот способ удобен тем, что в метод Удалить() можно передавать не только числовой индекс строки, но и саму ссылку на объект СтрокаТаблицыЗначений.
Если обработка данных выполняется на сервере, отличным решением будет применение объекта ПостроительЗапроса. Этот объект позволяет применять к таблицам значений сложные отборы, включая проверку вхождения в список, сравнение на больше или меньше, а также диапазоны.
Разберем по шагам, как настроить отбор по списку значений:
ПостроительЗапроса и передаем таблицу через ОписаниеИсточникаДанных.ВидСравнения.ВСписке.Посмотрим на практический пример кода:
СписокКоличеств = Новый Массив;
СписокКоличеств.Добавить(1);
СписокКоличеств.Добавить(2);
Построитель = Новый ПостроительЗапроса;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(тз_Осн);
ЭлементОтбора = Построитель.Отбор.Добавить("Количество");
ЭлементОтбора.Использование = Истина;
ЭлементОтбора.ВидСравнения = ВидСравнения.ВСписке;
ЭлементОтбора.Значение = СписокКоличеств;
тзРез = Построитель.Результат.Выгрузить();
Важный нюанс: ПостроительЗапроса доступен исключительно в серверном контексте, толстом клиенте или во внешнем соединении. На тонком клиенте этот объект вызвать нельзя.
Обратим внимание на вопросы производительности. Метод Удалить() при каждом вызове физически сдвигает нижележащие строки в оперативной памяти. Если исходная таблица содержит десятки или сотни тысяч записей, поштучное удаление строк (даже в обратном цикле) вызовет ощутимые задержки из-за квадратичной сложности алгоритма.
В высоконагруженных сценариях гораздо быстрее создать пустую таблицу с аналогичной структурой колонок и перенести в нее только нужные строки:
тзРез = тз_Осн.СкопироватьКолонки();
Для Каждого СтрИсходная Из тз_Осн Цикл
Если СтрИсходная.Количество = 1 Или СтрИсходная.Количество = 2 Тогда
ЗаполнитьЗначенияСвойств(тзРез.Добавить(), СтрИсходная);
КонецЕсли;
КонецЦикла;
Такой подход исключает накладные расходы на постоянную перестройку массива строк в памяти и работает значительно быстрее на больших объемах данных.
Таблицу значений можно передать параметром в обычный объект Запрос, поместить ее во временную таблицу и применить стандартную секцию ГДЕ языка запросов 1С.
Однако здесь существует строгое ограничение: чтобы таблица значений могла быть помещена во временную таблицу (конструкция ПОМЕСТИТЬ ВтТаблица), все используемые в запросе колонки обязательно должны иметь явно заданное описание типов (объект ОписаниеТипов). Если тип колонки не определен или составной без явных ограничений, платформа вернет ошибку на этапе выполнения запроса к СУБД.
Для небольших локальных таблиц без строгой типизации колонок оптимальнее использовать удаление в обратном цикле или фильтрацию через ПостроительЗапроса.