К статье
Практический материал

Разработка и Конфигурация · 9 мин

Как обойти табличную часть в 1С: методы, примеры кода и типичные ошибки

Как обойти табличную часть в 1С: цикл Для Каждого, методы НайтиСтроки, Итог, выгрузка в таблицу значений. Примеры кода, ошибки и рекомендации.

Обход табличной части — одна из самых частых задач при доработке конфигураций 1С: подсчёт сумм, проверка заполнения строк, перенос данных между документами, формирование печатных форм. Встроенный язык предоставляет несколько способов перебора строк, и выбор конкретного влияет на скорость выполнения и удобство поддержки кода.

Табличная часть объекта (документа, справочника, обработки) представляет собой коллекцию строк. К каждой строке можно обратиться по индексу, перебрать в цикле, отфильтровать или выгрузить в таблицу значений для дальнейшей обработки. Ниже разобраны основные приёмы с примерами кода, которые работают как на обычных, так и на управляемых формах.

Базовый обход циклом «Для Каждого»

Самый читаемый и безопасный способ — перебор коллекции через «Для Каждого». Внутри цикла переменная содержит ссылку на текущую строку, и все её реквизиты доступны напрямую.

Для Каждого СтрокаТовары Из Объект.Товары Цикл

Если СтрокаТовары.Количество = 0 Тогда

Сообщить("В строке " + СтрокаТовары.НомерСтроки + " не указано количество");

КонецЕсли;

КонецЦикла;

Обратите внимание: у строки табличной части всегда есть служебное свойство НомерСтроки — оно формируется платформой автоматически и удобно для сообщений пользователю. Изменять реквизиты строки внутри такого цикла можно: изменения сохранятся в объекте, если вы работаете с реквизитом формы или объектом данных, а не с копией.

Цикл «Для Каждого» подходит, когда нужно обработать все строки последовательно: пересчитать суммы, проверить заполнение, собрать массив значений. Это основной приём, к которому стоит прибегать по умолчанию.

Обход по индексу и цикл «Пока»

Когда нужен доступ к номеру строки, соседним строкам или требуется пропускать элементы с нестандартным шагом, используют цикл со счётчиком. Индексация строк в коллекции начинается с нуля.

КоличествоСтрок = Объект.Товары.Количество();

Для Индекс = 0 По КоличествоСтрок - 1 Цикл

СтрокаТовары = Объект.Товары[Индекс];

// обработка строки

КонецЦикла;

Типичная ошибка — обращение к Товары[КоличествоСтрок]: такого индекса не существует, и платформа выдаст ошибку «Индекс находится за границами массива». Всегда вычитайте единицу из количества при прямой индексации.

Поиск строк по условию: НайтиСтроки

Если нужны не все строки, а только соответствующие условию, метод НайтиСтроки() вернёт массив строк без ручного перебора. Параметры отбора передаются структурой.

Отбор = Новый Структура("Склад", СкладОтгрузки);

НайденныеСтроки = Объект.Товары.НайтиСтроки(Отбор);

Для Каждого СтрокаТовары Из НайденныеСтроки Цикл

СтрокаТовары.Цена = 0;

КонецЦикла;

Метод удобен, но имеет ограничение: отбор работает только на равенство значений. Условия вида «количество больше нуля» или «сумма в диапазоне» через НайтиСтроки не выразить — придётся использовать обычный цикл с проверкой или выгрузку в таблицу значений с запросом.

Для поиска одной строки существует метод Найти(Значение, ИмяКолонки), который возвращает первую подходящую строку или Неопределено. Всегда проверяйте результат на Неопределено перед обращением к реквизитам строки.

Агрегаты без обхода: Итог и Свернуть

Частая ситуация: разработчик пишет цикл, чтобы посчитать сумму по колонке, хотя платформа делает это одним вызовом. Метод Итог("Сумма") возвращает итог по числовой колонке за постоянное время относительно ручного перебора.

СуммаДокумента = Объект.Товары.Итог("Сумма");

ВсегоКоличество = Объект.Товары.Итог("Количество");

Если нужны итоги в разрезе групп (по номенклатуре, по складам), выгрузите табличную часть и используйте Свернуть():

ТаблицаИтогов = Объект.Товары.Выгрузить();

ТаблицаИтогов.Свернуть("Номенклатура", "Количество,Сумма");

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

Когда строк нужно много и обработка нетривиальна — группировки, сортировка, соединение с другими данными, — эффективнее выгрузить табличную часть в таблицу значений или передать её в запрос.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Номенклатура КАК Номенклатура,

| СУММА(Товары.Сумма) КАК Сумма

|ПОМЕСТИТЬ ВТ_Товары

|ИЗ &Товары КАК Товары

|;

|ВЫБРАТЬ * ИЗ ВТ_Товары КАК ВТ_Товары";

Запрос.УстановитьПараметр("Товары", Объект.Товары.Выгрузить());

Результат = Запрос.Выполнить().Выбрать();

Такой подход оправдан при объёмах от нескольких сотен строк и при необходимости соединить данные табличной части с регистрами или справочниками. Для десятка строк запрос будет избыточен — простой цикл проще и быстрее в сопровождении.

Сравнение способов обхода

Для Каждого

Универсальный вариант для всех строк. Простой и читаемый, но без индекса из коробки.

По индексу

Нужен доступ к номеру, соседям или удаление с конца. Требует аккуратности с границами.

НайтиСтроки

Отбор по равенству значений. Не поддерживает сложные условия.

Выгрузить + запрос

Большие объёмы, группировки, связи с другими данными. Избыточен для малых таблиц.

Обход табличной части на управляемой форме

На управляемой форме табличная часть документа обычно доступна как реквизит формы типа «ДанныеФормыКоллекция». Обход выглядит так же — через «Для Каждого», — но есть нюансы с контекстом сервера.

Метод Выгрузить() у ДанныеФормыКоллекция недоступен в клиентском контексте без серверного вызова, а Итог() работает. Если нужна таблица значений на сервере, передавайте объект через РеквизитФормыВЗначение("Объект") и работайте с ним как с обычным объектом.

Для текущей (выделенной) строки таблицы формы используйте Элементы.Товары.ТекущиеДанные — обход не требуется, если задача касается только редактируемой строки.

Порядок выбора способа обхода
  1. 1
    Определите задачу: обработать все строки, найти часть, посчитать итог или удалить строки
  2. 2
    Для всех строк — используйте «Для Каждого»
  3. 3
    Для отбора по равенству — НайтиСтроки со структурой отбора
  4. 4
    Для итога по числовой колонке — Итог() без цикла
  5. 5
    Для удаления — цикл по индексу с конца коллекции
  6. 6
    Для группировок и связей — Выгрузить() и запрос

Типичные ошибки при обходе

Типичные ошибки
01
Удаление строк в прямом цикле — часть строк пропускается из-за сдвига индексов
02
Обращение к Товары[Товары.Количество()] — ошибка выхода за границы, индексация с нуля
03
Ручной подсчёт суммы в цикле вместо Итог() — лишний код и медленнее
04
Использование Найти() без проверки на Неопределено — ошибка обращения к реквизиту
05
Обход Выгрузить() ради простого пересчёта — лишние затраты памяти на копию данных

Отдельно стоит упомянуть модификацию коллекции во время обхода: добавление строк в цикле «Для Каждого» по той же коллекции приведёт к бесконечному циклу или непредсказуемому поведению. Если нужно дополнить табличную часть по результатам анализа, сначала соберите данные в массив или таблицу значений, а добавление выполняйте после завершения обхода.

Контрольные точки

Как проверить результат

Все строки обработаны
Норма

Счётчик обработанных равен Количество() табличной части

Тревожный признак

Обработано меньше строк — вероятно удаление или сдвиг в цикле

Итог по колонке
Норма

Итог() совпадает с суммой в подвале таблицы формы

Тревожный признак

Расхождение — проверьте тип колонки и пустые значения

Поиск строк
Норма

НайденныеСтроки.Количество() больше нуля и строки соответствуют отбору

Тревожный признак

Пустой массив — проверьте значение отбора и тип данных колонки

Частые вопросы

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

Используйте цикл по индексу с конца: Для Индекс = 0 По КоличествоСтрок - 1 с обращением Товары[КоличествоСтрок - 1 - Индекс], либо убывающий счётчик через цикл «Пока». Обратный порядок обязателен при удалении строк.

Можно ли прервать обход досрочно?

Да, оператор «Прервать» завершает цикл, «Продолжить» переходит к следующей итерации. Например, при поиске первой строки с ошибкой заполнения имеет смысл прервать цикл сразу после нахождения, чтобы не тратить время на остальные строки.

Чем отличается обход табличной части объекта от выгруженной таблицы значений?

По синтаксису — почти ничем, «Для Каждого» работает одинаково. Различие в данных: изменения в строках табличной части объекта попадут в базу при записи, а таблица значений — это независимая копия в памяти, изменения которой никуда не сохраняются без обратной загрузки.

Как обойти сразу несколько табличных частей документа?

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

Что быстрее: цикл или запрос по выгруженной таблице?

Для небольших табличных частей быстрее и проще цикл — нет накладных расходов на выгрузку и выполнение запроса. Запрос выигрывает на больших объёмах и когда нужны соединения с данными базы. Точный порог зависит от конкретной задачи, ориентируйтесь на замеры в своей среде.

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

Используйте Объект.Товары.ВыгрузитьКолонку("Номенклатура") — метод вернёт массив значений указанной колонки. Для данных формы аналогичного метода нет, там потребуется цикл или серверный вызов с преобразованием в значение.

Итог

Что важно запомнить

Базовый приём — цикл «Для Каждого». Итоги считайте через Итог(), отбор — через НайтиСтроки(), удаление выполняйте с конца коллекции, а сложную аналитику выносите в запрос по выгруженной таблице значений.