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

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

Как перебрать элементы табличной части в 1С

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

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

Выбор способа зависит от задачи: если нужно просто обработать каждую строку, достаточно цикла по коллекции; если требуется удаление строк во время обхода или доступ к номеру строки — понадобится цикл со счётчиком. Ниже разобраны рабочие варианты кода, их ограничения и типичные ошибки, которые приводят к пропуску строк или к ошибкам исполнения.

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

Стандартный и рекомендуемый способ обхода всех строк табличной части — цикл Для Каждого ... Цикл. Он читаем, безопасен и не требует ручного управления индексами:

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

СтрокаТовары.Сумма = СтрокаТовары.Количество * СтрокаТовары.Цена;

КонецЦикла;

В переменной СтрокаТовары на каждой итерации находится объект строки табличной части. Изменение её реквизитов сразу изменяет данные объекта — отдельно «записывать» строку обратно не нужно. Если код выполняется в модуле формы документа, обращение идёт через Объект.Товары; в модуле объекта или внешней обработке — через переменную, содержащую объект документа, например ДокументОбъект.Товары.

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

1c
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    Если НЕ ЗначениеЗаполнено(СтрокаТЧ.Номенклатура) Тогда
        Сообщить("Не заполнена номенклатура в строке " + СтрокаТЧ.НомерСтроки);
    КонецЕсли;
КонецЦикла;

Перебор по индексу: когда нужен номер строки

Если внутри цикла нужен индекс строки или планируется изменять состав коллекции, используйте классический цикл со счётчиком. Индексация строк в коллекции начинается с нуля, а метод Количество() возвращает число строк:

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

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

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

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

КонецЦикла;

Обратите внимание на различие двух «номеров»: индекс строки в коллекции начинается с 0, а реквизит НомерСтроки, который видит пользователь в таблице, начинается с 1. Связь простая: НомерСтроки = Индекс + 1, но при удалении строк платформа пересчитывает номера автоматически, а индексы сдвигаются — это источник частых ошибок.

Удаление строк во время перебора

Удалять элементы коллекции внутри цикла Для Каждого нельзя — это приводит к пропуску строк или ошибке, потому что коллекция изменяется во время итерации. Правильный приём — обход по индексу с конца коллекции:

Для Индекс = 1 По Объект.Товары.Количество() Цикл

СтрокаТовары = Объект.Товары[Объект.Товары.Количество() - Индекс];

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

Объект.Товары.Удалить(СтрокаТовары);

КонецЕсли;

КонецЦикла;

Обход с конца гарантирует, что удаление текущей строки не сдвинет индексы ещё не обработанных элементов. Альтернатива — сначала собрать строки-кандидаты в массив, а затем удалить их отдельным циклом:

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

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

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

СтрокиКУдалению.Добавить(СтрокаТовары);

КонецЕсли;

КонецЦикла;

Для Каждого СтрокаКУдалению Из СтрокиКУдалению Цикл

Объект.Товары.Удалить(СтрокаКУдалению);

КонецЦикла;

Типичные ошибки
01
Удаление строк внутри цикла «Для Каждого» — часть строк пропускается или возникает ошибка обхода коллекции
02
Обход по индексу от 1 до Количество() — выход за границы коллекции, так как индексация идёт с нуля
03
Использование индекса вместо НомерСтроки в сообщениях пользователю — человек ищет несуществующую строку
04
Изменение объекта без последующего Записать() в серверном коде — изменения теряются

Поиск и отбор строк без полного перебора

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

Отбор = Новый Структура("Номенклатура", ИскомаяНоменклатура);

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

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

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

КонецЦикла;

Для подсчёта итогов по числовой колонке есть метод Итог() — он заменяет цикл с накоплением суммы и работает быстрее и нагляднее:

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

Метод Найти() возвращает одну первую найденную строку или Неопределено, если совпадений нет. Всегда проверяйте результат на Неопределено перед обращением к реквизитам строки, иначе получите ошибку «Поле объекта не обнаружено».

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

Для Каждого

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

Цикл по индексу

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

НайтиСтроки / Найти

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

Итог()

Быстрая сумма по числовой колонке. Только агрегация, без доступа к строкам

Перебор на клиенте и на сервере

В управляемых формах код делится на клиентский и серверный контекст. Перебор табличной части в директиве &НаКлиенте выполняется без обращения к серверу и подходит для интерактивных пересчётов — например, обновления суммы при изменении цены. Данные табличной части на клиенте доступны через Объект.Товары точно так же.

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

Проверка результата и отладка обхода

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

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

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

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

Пустая табличная часть
Норма

Цикл не выполняется, ошибок нет

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

Ошибка обращения по индексу или деление на ноль

Удаление строк по условию
Норма

Удалены все подходящие строки, остальные на месте

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

Часть строк пропущена — обход шёл с начала при удалении

Номера в сообщениях
Норма

Номер совпадает с видимым в форме

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

Номер смещён на единицу — использован индекс

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

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

Используйте метод Количество(): Объект.Товары.Количество(). Для проверки на пустоту достаточно сравнения с нулём. В управляемой форме тот же метод работает и на клиенте.

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

Сначала получите объект: ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект(). Затем перебирайте ДокОбъект.Товары. После изменений не забудьте вызвать ДокОбъект.Записать(), иначе правки не сохранятся.

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

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

Почему при удалении строк в цикле часть строк остаётся?

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

Как перебрать строки только видимые после отбора в форме?

Отбор в элементе формы влияет только на отображение. Чтобы обойти отфильтрованные строки, получите параметры отбора из элемента формы и примените НайтиСтроки() с аналогичной структурой отбора к данным табличной части.

Чем перебор отличается от запроса к табличной части?

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

Итог

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

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