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

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

Что такое итератор в 1С 8 и как его использовать

Итератор в 1С 8: что это, как работает обход коллекций через Для Каждого, чем итератор отличается от индекса и когда какой способ перебора выбрать.

Итератор в 1С 8 — это механизм последовательного перебора элементов коллекции: массива, таблицы значений, списка значений, выборки из запроса или коллекции строк дерева. В отличие от языков вроде C# или Java, в 1С нет отдельного объекта с явным типом «Итератор» и методами вроде Next() — итератор реализован внутри конструкции «Для Каждого … Из … Цикл», которая на каждом шаге помещает очередной элемент коллекции в указанную переменную.

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

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

Как устроен обход коллекции через «Для Каждого»

Основная конструкция перебора в 1С выглядит так: «Для Каждого Элемент Из Коллекция Цикл … КонецЦикла». Переменная, указанная после «Для Каждого», — это и есть переменная-итератор. На каждом шаге платформа присваивает ей ссылку на очередной элемент коллекции, и внутри тела цикла доступны все свойства и методы этого элемента.

Пример: при переборе таблицы значений конструкция «Для Каждого СтрокаТЗ Из ТаблицаЗначений Цикл» даёт в переменной «СтрокаТЗ» объект типа «СтрокаТаблицыЗначений». Можно сразу обращаться к колонкам: СтрокаТЗ.Номенклатура, СтрокаТЗ.Количество. При переборе массива переменная получает само значение элемента, при переборе структуры — объект «КлючИЗначение» с полями «Ключ» и «Значение».

Важная особенность: для объектов-ссылок (строк таблицы значений, строк дерева) переменная-итератор содержит ссылку на живой элемент, поэтому изменение её свойств меняет саму коллекцию. Для массивов примитивных типов переменная получает копию значения — присваивание ей нового значения не изменит элемент массива.

1c
Для Каждого СтрокаТЗ Из ТаблицаТоваров Цикл
    Если СтрокаТЗ.Количество = 0 Тогда
        Продолжить;
    КонецЕсли;
    СтрокаТЗ.Сумма = СтрокаТЗ.Количество * СтрокаТЗ.Цена;
КонецЦикла;

Чем итератор отличается от перебора по индексу

Второй способ обхода — классический цикл со счётчиком: «Для Индекс = 0 По Коллекция.Количество() - 1 Цикл». Здесь никакого итератора нет: разработчик сам управляет позицией через числовой индекс и обращается к элементу как Коллекция[Индекс]. Оба подхода решают одну задачу, но у них разные сильные стороны.

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

Сравнение вариантов перебора

Для Каждого (итератор)

Подходит для большинства задач: короткий синтаксис, нет риска выхода за границы. Ограничение: нет доступа к индексу и неудобно идти в обратном порядке.

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

Нужен, когда важна позиция элемента, обратный порядок обхода или синхронный перебор нескольких коллекций. Требует аккуратности с границами: индексация в 1С идёт с нуля.

Обход результата запроса

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

Особенности итераторов для разных типов коллекций

Массив перебирается напрямую, переменная цикла получает значение элемента. Если элементы — примитивы (числа, строки), изменения переменной не влияют на массив; если элементы — объекты (например, строки таблицы значений), изменения их свойств отражаются на содержимом.

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

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

Удаление и изменение элементов во время перебора

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

Безопасные приёмы таковы. Первый — собрать удаляемые элементы в отдельный массив внутри цикла, а удалить их после его завершения. Второй — перебирать коллекцию по индексу в обратном порядке (от последнего элемента к первому): тогда удаление текущего элемента не влияет на позиции ещё не просмотренных. Третий — для таблиц значений использовать методы НайтиСтроки() с отбором и удалять найденные строки отдельным циклом.

Изменение свойств текущего элемента (без удаления и добавления) при переборе «Для Каждого» безопасно: итератор держит ссылку на элемент, и присваивания вроде СтрокаТЗ.Сумма = … отрабатывают корректно. Проблемы возникают только при изменении состава коллекции — добавлении или удалении элементов.

Производительность и выбор способа обхода

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

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

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

Типичные ошибки
01
Удаление строк таблицы значений внутри цикла «Для Каждого» — часть строк пропускается, данные обрабатываются не полностью.
02
Ожидание, что изменение переменной цикла изменит элемент массива примитивов: Для Каждого Элемент Из Массив с последующим Элемент = 0 не меняет массив.
03
Попытка перебрать выборку запроса второй раз — она уже исчерпана, цикл не выполнится ни разу.
04
Цикл «Для Индекс = 1 По Количество» вместо «0 По Количество - 1»: индексация коллекций в 1С начинается с нуля, первый элемент пропускается, а последний вызывает ошибку выхода за границы.
05
Выполнение запроса внутри цикла перебора вместо одного пакетного запроса — резкое падение производительности.

Практический пример: обработка таблицы значений

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

Порядок действий
  1. 1
    Объявите массив для строк, подлежащих удалению.
  2. 2
    Переберите таблицу через «Для Каждого СтрокаТЗ Из Таблица Цикл», выполните расчёты и добавьте пустые строки в массив удаления.
  3. 3
    После цикла пройдите по массиву удаления и вызовите Таблица.Удалить(Строка) для каждой строки.
  4. 4
    Проверьте результат: Таблица.Количество() и контрольную сумму по итоговой колонке.

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

Итог

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

Итератор в 1С — это механизм перебора внутри «Для Каждого … Из … Цикл», а не отдельный объект. Используйте его для чтения и изменения элементов, цикл по индексу — когда нужна позиция или обратный порядок. Никогда не меняйте состав коллекции внутри её собственного перебора: сначала соберите, потом удалите.

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

Есть ли в 1С объект с явными методами итератора, как в других языках?

Нет. В платформе 1С 8 нет типа «Итератор» с методами вроде ПолучитьСледующий() или ЕстьСледующий(). Перебор реализован конструкцией «Для Каждого … Из … Цикл», а управление позицией полностью берёт на себя платформа. Ближайший аналог ручного итератора — метод Следующий() у выборки из запроса.

Можно ли узнать индекс текущего элемента при переборе «Для Каждого»?

Напрямую — нет. Варианты обхода: завести переменную-счётчик и увеличивать её в теле цикла, использовать метод Индекс() у таблицы значений (Таблица.Индекс(СтрокаТЗ)) или перейти на цикл по индексу, если позиция нужна постоянно.

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

Не удаляйте строки внутри «Для Каждого». Либо соберите строки в массив и удалите после цикла, либо используйте Таблица.НайтиСтроки(Отбор) и удаляйте результат, либо перебирайте таблицу по индексу в обратном порядке от последней строки к первой.

Почему второй цикл по выборке из запроса не выполняется?

Выборка — однонаправленный итератор: после полного обхода она исчерпана. Чтобы использовать данные повторно, выгрузите результат в таблицу значений методом Результат.Выгрузить() и перебирайте уже её сколько угодно раз.

Что быстрее: «Для Каждого» или цикл по индексу?

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

Изменит ли присваивание переменной цикла сам элемент коллекции?

Зависит от типа. Для строк таблицы значений и других объектов-ссылок изменение свойств переменной меняет элемент коллекции. Для примитивных значений в массиве переменная получает копию — чтобы изменить массив, нужно писать Массив[Индекс] = НовоеЗначение.