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

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

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

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

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

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

Цикл «Для Каждого» — основной способ перебора

Цикл Для Каждого последовательно помещает каждый элемент массива в указанную переменную. Это самый короткий и читаемый вариант, когда порядковый номер элемента не нужен.

МассивТоваров = Новый Массив;

МассивТоваров.Добавить("Номенклатура 1");

МассивТоваров.Добавить("Номенклатура 2");

Для Каждого Товар Из МассивТоваров Цикл

Сообщить(Товар);

КонецЦикла;

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

Перебор по индексу через цикл «Для»

Когда нужна позиция элемента или возможность его замены, используется цикл Для со счётчиком. Индексация массивов в 1С начинается с нуля, поэтому последний элемент имеет индекс Количество() - 1.

Для Инд = 0 По МассивТоваров.ВГраница() Цикл

МассивТоваров[Инд] = СокрЛП(МассивТоваров[Инд]);

КонецЦикла;

Метод ВГраница() возвращает индекс последнего элемента и безопаснее, чем выражение Количество() - 1: для пустого массива ВГраница() вернёт -1, и цикл просто не выполнится. Обращение к элементу Массив[Инд] допустимо и для чтения, и для записи.

Обход массива в обратном порядке

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

Для Инд = -МассивТоваров.ВГраница() По 0 Цикл

РеальныйИндекс = -Инд;

Если ПустаяСтрока(МассивТоваров[РеальныйИндекс]) Тогда

МассивТоваров.Удалить(РеальныйИндекс);

КонецЕсли;

КонецЦикла;

Трюк с отрицательным счётчиком — стандартная практика в 1С, поскольку цикл Для не поддерживает шаг -1 напрямую. Альтернатива — собирать индексы удаляемых элементов в отдельный массив и удалять их после основного цикла.

Типичные ошибки
01
Удаление элементов при прямом обходе — из-за сдвига индексов пропускаются соседние элементы
02
Выход за границы массива при использовании Количество() вместо ВГраница() или Количество()-1
03
Попытка изменить элемент массива через переменную цикла «Для Каждого» — изменение теряется
04
Пересчёт Массив.Количество() внутри тела цикла при удалении элементов — граница цикла вычисляется один раз, что приводит к обращению за пределы массива

Поиск и фильтрация элементов при переборе

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

Индекс = МассивТоваров.Найти("Номенклатура 2");

Если Индекс <> Неопределено Тогда

Сообщить("Найдено на позиции " + Индекс);

КонецЕсли;

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

Отобранные = Новый Массив;

Для Каждого Строка Из МассивДанных Цикл

Если Строка.Сумма > 10000 Тогда

Отобранные.Добавить(Строка);

КонецЕсли;

КонецЦикла;

Маршрут решения

Как выбрать способ обхода

Нужно только прочитать или обработать каждый элементИспользуйте «Для Каждого Элемент Из Массив Цикл»
Нужен индекс элемента или замена значения в массивеИспользуйте «Для Инд = 0 По Массив.ВГраница() Цикл»
Элементы удаляются во время обходаИдите от конца массива к началу через отрицательный счётчик
Нужно найти точное совпадение значенияИспользуйте Массив.Найти(Значение) без цикла

Перебор вложенных и многомерных массивов

Массив в 1С может содержать другие массивы — так моделируют двумерные структуры, например матрицу строк и колонок. Перебор выполняется вложенными циклами.

Для Каждого СтрокаМассива Из Матрица Цикл

Для Каждого Ячейка Из СтрокаМассива Цикл

Сообщить(Ячейка);

КонецЦикла;

КонецЦикла;

Перед обращением к вложенному элементу полезно проверять тип значения через ТипЗнч(), если структура данных не гарантирована. Сравнение идёт с Тип("Массив"). Это защищает от ошибки «Итератор для значения не определен», которая возникает при попытке перебрать значение, не являющееся коллекцией.

Контроль производительности при больших массивах

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

Основные правила оптимизации: не выполняйте запросы внутри цикла — соберите данные массива в одном запросе с конструкцией В (&СписокЗначений); для поиска по большому числу элементов замените массив на соответствие, где поиск по ключу выполняется быстро; кэшируйте результаты Количество() в переменную, если метод вызывается многократно.

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

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

Убедиться, что перебор отработал корректно, помогают простые приёмы. В отладчике конфигуратора поставьте точку останова внутри цикла и следите за значением переменной цикла и счётчика итераций. Для проверки без отладчика добавьте временный счётчик обработанных элементов и сравните его с Массив.Количество() после цикла.

Типичный признак корректной работы: число итераций равно размеру массива (или ожидаемому числу отобранных элементов), а обращений за границы массива не возникает. Ошибка «Индекс находится за границами массива» почти всегда означает, что граница цикла рассчитана неверно либо массив изменялся во время обхода.

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

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

Число итераций цикла
Норма

Равно Массив.Количество() или ожидаемому числу отобранных элементов

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

Меньше размера массива — возможен пропуск из-за удаления элементов

Обращение по индексу
Норма

Индекс в диапазоне от 0 до Массив.ВГраница()

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

Ошибка «Индекс находится за границами массива»

Изменение элементов
Норма

Значения в массиве обновлены после цикла

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

Массив не изменился — правилась переменная цикла, а не элемент

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

Чем «Для Каждого» отличается от «Для» при переборе массива?

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

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

Напрямую — никак. Либо заведите отдельную переменную-счётчик и увеличивайте её на каждой итерации, либо перейдите на цикл «Для Инд = 0 По Массив.ВГраница() Цикл», где индекс доступен сразу.

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

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

Почему возникает ошибка «Итератор для значения не определен»?

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

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

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

Что быстрее для поиска элемента — перебор или соответствие?

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

Итог

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

Для чтения элементов используйте «Для Каждого», для изменения и удаления — цикл «Для» с индексом от 0 до ВГраница(). Удаляйте элементы только при обратном обходе, не выполняйте запросы внутри цикла и проверяйте тип значения перед перебором вложенных структур.