Разработка и Конфигурация · 9 мин
Как обойти табличную часть в 1С: методы, примеры кода и типичные ошибки
Как обойти табличную часть в 1С: цикл Для Каждого, методы НайтиСтроки, Итог, выгрузка в таблицу значений. Примеры кода, ошибки и рекомендации.
Обход табличной части — одна из самых частых задач при доработке конфигураций 1С: подсчёт сумм, проверка заполнения строк, перенос данных между документами, формирование печатных форм. Встроенный язык предоставляет несколько способов перебора строк, и выбор конкретного влияет на скорость выполнения и удобство поддержки кода.
Табличная часть объекта (документа, справочника, обработки) представляет собой коллекцию строк. К каждой строке можно обратиться по индексу, перебрать в цикле, отфильтровать или выгрузить в таблицу значений для дальнейшей обработки. Ниже разобраны основные приёмы с примерами кода, которые работают как на обычных, так и на управляемых формах.
Базовый обход циклом «Для Каждого»
Самый читаемый и безопасный способ — перебор коллекции через «Для Каждого». Внутри цикла переменная содержит ссылку на текущую строку, и все её реквизиты доступны напрямую.
Для Каждого СтрокаТовары Из Объект.Товары Цикл
Если СтрокаТовары.Количество = 0 Тогда
Сообщить("В строке " + СтрокаТовары.НомерСтроки + " не указано количество");
КонецЕсли;
КонецЦикла;
Обратите внимание: у строки табличной части всегда есть служебное свойство НомерСтроки — оно формируется платформой автоматически и удобно для сообщений пользователю. Изменять реквизиты строки внутри такого цикла можно: изменения сохранятся в объекте, если вы работаете с реквизитом формы или объектом данных, а не с копией.
Цикл «Для Каждого» подходит, когда нужно обработать все строки последовательно: пересчитать суммы, проверить заполнение, собрать массив значений. Это основной приём, к которому стоит прибегать по умолчанию.
Обход по индексу и цикл «Пока»
Когда нужен доступ к номеру строки, соседним строкам или требуется пропускать элементы с нестандартным шагом, используют цикл со счётчиком. Индексация строк в коллекции начинается с нуля.
КоличествоСтрок = Объект.Товары.Количество();
Для Индекс = 0 По КоличествоСтрок - 1 Цикл
СтрокаТовары = Объект.Товары[Индекс];
// обработка строки
КонецЦикла;
Типичная ошибка — обращение к Товары[КоличествоСтрок]: такого индекса не существует, и платформа выдаст ошибку «Индекс находится за границами массива». Всегда вычитайте единицу из количества при прямой индексации.
Поиск строк по условию: НайтиСтроки
Если нужны не все строки, а только соответствующие условию, метод НайтиСтроки() вернёт массив строк без ручного перебора. Параметры отбора передаются структурой.
Отбор = Новый Структура("Склад", СкладОтгрузки);
НайденныеСтроки = Объект.Товары.НайтиСтроки(Отбор);
Для Каждого СтрокаТовары Из НайденныеСтроки Цикл
СтрокаТовары.Цена = 0;
КонецЦикла;
Метод удобен, но имеет ограничение: отбор работает только на равенство значений. Условия вида «количество больше нуля» или «сумма в диапазоне» через НайтиСтроки не выразить — придётся использовать обычный цикл с проверкой или выгрузку в таблицу значений с запросом.
Для поиска одной строки существует метод Найти(Значение, ИмяКолонки), который возвращает первую подходящую строку или Неопределено. Всегда проверяйте результат на Неопределено перед обращением к реквизитам строки.
Агрегаты без обхода: Итог и Свернуть
Частая ситуация: разработчик пишет цикл, чтобы посчитать сумму по колонке, хотя платформа делает это одним вызовом. Метод Итог("Сумма") возвращает итог по числовой колонке за постоянное время относительно ручного перебора.
СуммаДокумента = Объект.Товары.Итог("Сумма");
ВсегоКоличество = Объект.Товары.Итог("Количество");
Если нужны итоги в разрезе групп (по номенклатуре, по складам), выгрузите табличную часть и используйте Свернуть():
ТаблицаИтогов = Объект.Товары.Выгрузить();
ТаблицаИтогов.Свернуть("Номенклатура", "Количество,Сумма");
Выгрузка в таблицу значений для сложной обработки
Когда строк нужно много и обработка нетривиальна — группировки, сортировка, соединение с другими данными, — эффективнее выгрузить табличную часть в таблицу значений или передать её в запрос.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| СУММА(Товары.Сумма) КАК Сумма
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ &Товары КАК Товары
|;
|ВЫБРАТЬ * ИЗ ВТ_Товары КАК ВТ_Товары";
Запрос.УстановитьПараметр("Товары", Объект.Товары.Выгрузить());
Результат = Запрос.Выполнить().Выбрать();
Такой подход оправдан при объёмах от нескольких сотен строк и при необходимости соединить данные табличной части с регистрами или справочниками. Для десятка строк запрос будет избыточен — простой цикл проще и быстрее в сопровождении.
Сравнение способов обхода
Универсальный вариант для всех строк. Простой и читаемый, но без индекса из коробки.
Нужен доступ к номеру, соседям или удаление с конца. Требует аккуратности с границами.
Отбор по равенству значений. Не поддерживает сложные условия.
Большие объёмы, группировки, связи с другими данными. Избыточен для малых таблиц.
Обход табличной части на управляемой форме
На управляемой форме табличная часть документа обычно доступна как реквизит формы типа «ДанныеФормыКоллекция». Обход выглядит так же — через «Для Каждого», — но есть нюансы с контекстом сервера.
Метод Выгрузить() у ДанныеФормыКоллекция недоступен в клиентском контексте без серверного вызова, а Итог() работает. Если нужна таблица значений на сервере, передавайте объект через РеквизитФормыВЗначение("Объект") и работайте с ним как с обычным объектом.
Для текущей (выделенной) строки таблицы формы используйте Элементы.Товары.ТекущиеДанные — обход не требуется, если задача касается только редактируемой строки.
- 1Определите задачу: обработать все строки, найти часть, посчитать итог или удалить строки
- 2Для всех строк — используйте «Для Каждого»
- 3Для отбора по равенству — НайтиСтроки со структурой отбора
- 4Для итога по числовой колонке — Итог() без цикла
- 5Для удаления — цикл по индексу с конца коллекции
- 6Для группировок и связей — Выгрузить() и запрос
Типичные ошибки при обходе
Отдельно стоит упомянуть модификацию коллекции во время обхода: добавление строк в цикле «Для Каждого» по той же коллекции приведёт к бесконечному циклу или непредсказуемому поведению. Если нужно дополнить табличную часть по результатам анализа, сначала соберите данные в массив или таблицу значений, а добавление выполняйте после завершения обхода.
Как проверить результат
Счётчик обработанных равен Количество() табличной части
Обработано меньше строк — вероятно удаление или сдвиг в цикле
Итог() совпадает с суммой в подвале таблицы формы
Расхождение — проверьте тип колонки и пустые значения
НайденныеСтроки.Количество() больше нуля и строки соответствуют отбору
Пустой массив — проверьте значение отбора и тип данных колонки
Частые вопросы
Как обойти табличную часть в обратном порядке?
Используйте цикл по индексу с конца: Для Индекс = 0 По КоличествоСтрок - 1 с обращением Товары[КоличествоСтрок - 1 - Индекс], либо убывающий счётчик через цикл «Пока». Обратный порядок обязателен при удалении строк.
Можно ли прервать обход досрочно?
Да, оператор «Прервать» завершает цикл, «Продолжить» переходит к следующей итерации. Например, при поиске первой строки с ошибкой заполнения имеет смысл прервать цикл сразу после нахождения, чтобы не тратить время на остальные строки.
Чем отличается обход табличной части объекта от выгруженной таблицы значений?
По синтаксису — почти ничем, «Для Каждого» работает одинаково. Различие в данных: изменения в строках табличной части объекта попадут в базу при записи, а таблица значений — это независимая копия в памяти, изменения которой никуда не сохраняются без обратной загрузки.
Как обойти сразу несколько табличных частей документа?
Последовательно: отдельный цикл для каждой табличной части. Если логика обработки одинаковая, вынесите её в общую процедуру и передавайте коллекцию строк параметром — так код не дублируется.
Что быстрее: цикл или запрос по выгруженной таблице?
Для небольших табличных частей быстрее и проще цикл — нет накладных расходов на выгрузку и выполнение запроса. Запрос выигрывает на больших объёмах и когда нужны соединения с данными базы. Точный порог зависит от конкретной задачи, ориентируйтесь на замеры в своей среде.
Как получить массив всех значений одной колонки без цикла?
Используйте Объект.Товары.ВыгрузитьКолонку("Номенклатура") — метод вернёт массив значений указанной колонки. Для данных формы аналогичного метода нет, там потребуется цикл или серверный вызов с преобразованием в значение.
Что важно запомнить
Базовый приём — цикл «Для Каждого». Итоги считайте через Итог(), отбор — через НайтиСтроки(), удаление выполняйте с конца коллекции, а сложную аналитику выносите в запрос по выгруженной таблице значений.