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

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

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

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

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

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

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

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

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

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

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

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

Для Каждого Элемент Из МассивТоваров Цикл

Сообщить(Элемент);

КонецЦикла;

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

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

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

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

КонецЦикла;

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

Когда нужен номер элемента, используют классический цикл со счётчиком. Индексация массивов в 1С начинается с нуля, а верхнюю границу возвращает метод ВГраница() — он равен количеству элементов минус один.

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

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

КонецЦикла;

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

Частая ошибка — писать верхнюю границу как Массив.Количество() вместо ВГраница(). Из-за нумерации с нуля это приводит к выходу за пределы массива и ошибке «Индекс находится за границами массива» на последней итерации.

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

Обратный обход и удаление элементов

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

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

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

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

Если МассивТоваров[РеальныйИндекс].ПометкаУдаления Тогда

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

КонецЕсли;

КонецЦикла;

Альтернатива — не удалять, а собрать новый массив только из нужных элементов. Этот подход проще читается и безопаснее, особенно когда условие отбора сложное:

Результат = Новый Массив;

Для Каждого Элемент Из МассивТоваров Цикл

Если Не Элемент.ПометкаУдаления Тогда

Результат.Добавить(Элемент);

КонецЕсли;

КонецЦикла;

Проверка массива перед обходом

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

Если ТипЗнч(МассивТоваров) = Тип("Массив")

И МассивТоваров.Количество() > 0 Тогда

// обход массива

КонецЕсли;

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

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

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

Цикл выполнился
Норма

Код внутри цикла отработал для каждого элемента, количество итераций равно Количество()

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

Цикл не выполнился ни разу — массив пуст или переменная равна Неопределено

Удаление элементов
Норма

Удалены ровно те элементы, что соответствуют условию, остальные на месте

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

Часть подходящих элементов осталась — признак пропуска из-за удаления в прямом обходе

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

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

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

Ошибка на последней итерации — верхняя граница задана как Количество()

Выбор способа обхода под задачу

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

ЗадачаРекомендуемый способПричина
Просто прочитать все элементыДля КаждогоКороче и читаемее, нет риска ошибки с индексом
Изменить значения примитивов в массивеДля по индексуЗапись возможна только через Массив[Инд]
Удалить часть элементовОбратный обход по индексуУдаление не сдвигает ещё не проверенные элементы
Нужен номер элементаДля по индексуИндекс доступен напрямую без отдельного счётчика
Отфильтровать элементыДля Каждого + новый массивБезопаснее удаления на лету
Маршрут решения

Как выбрать действие

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

Производительность и особенности

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

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

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

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

С чего начинается нумерация элементов массива в 1С?

Индексация начинается с нуля: первый элемент — Массив[0], последний — Массив[Массив.ВГраница()]. Метод ВГраница() возвращает Количество() минус один.

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

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

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

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

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

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

Что произойдёт, если обойти пустой массив?

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

Чем обход массива отличается от обхода таблицы значений?

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

Итог

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

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