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