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