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

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

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

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

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

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

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

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

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

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

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

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

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

КонецЦикла;

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

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

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

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

Для Индекс = 0 По МассивТоваров.Количество() - 1 Цикл

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

КонецЦикла;

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

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

Удаление элементов: обход с конца

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

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

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

// Альтернатива: Для Индекс = 0 По Массив.ВГраница() с шагом назад не работает —

// используйте явное вычисление:

КонецЦикла;

// Правильный вариант обхода с конца:

КоличествоЭлементов = Массив.Количество();

Для Счетчик = 1 По КоличествоЭлементов Цикл

Индекс = КоличествоЭлементов - Счетчик;

Если Не УсловиеОтбора(Массив[Индекс]) Тогда

Массив.Удалить(Индекс);

КонецЕсли;

КонецЦикла;

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

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

Для Каждого

Лучший выбор для чтения данных. Не подходит для удаления и изменения элементов по индексу.

Для по индексу

Нужен, когда требуется номер позиции, изменение элемента или сравнение с соседями.

Обход с конца

Обязателен при удалении элементов по условию внутри цикла.

Новый массив-результат

Альтернатива удалению: безопасна, читаема, но требует дополнительной памяти.

Обход многомерных и вложенных структур

В 1С нет настоящих многомерных массивов — их имитируют массивом массивов. Обход такой конструкции выполняется вложенными циклами.

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

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

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

КонецЦикла;

КонецЦикла;

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

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

Обход массива с вычислениями и прерыванием

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

НайденныйЭлемент = Неопределено;

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

Если Элемент.Код = ИскомыйКод Тогда

НайденныйЭлемент = Элемент;

Прервать;

КонецЕсли;

КонецЦикла;

Если нужен только факт наличия или позиция элемента, у массива есть встроенный метод «Найти(Значение)» — он вернёт индекс первого вхождения или «Неопределено». Это короче и быстрее ручного цикла, но работает только для точного совпадения значения, без сложных условий.

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

Порядок действий при выборе способа обхода
  1. 1
    Определите, нужно ли изменять состав массива (добавлять или удалять элементы)
  2. 2
    Если состав не меняется и индекс не нужен — используйте «Для Каждого»
  3. 3
    Если нужен индекс или изменение значений — используйте «Для» от 0 до Количество() - 1
  4. 4
    Если удаляете элементы — обходите массив с конца или собирайте новый массив
  5. 5
    Проверьте результат: пройдены ли все элементы и не пропущен ли ни один

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

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

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

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

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

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

После написания цикла убедитесь, что обработано ровно столько элементов, сколько ожидалось. Простой приём — счётчик итераций, который в отладочном режиме сравнивается с «Массив.Количество()». Если цикл прерывается по условию, счётчик покажет, на каком элементе произошла остановка.

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

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

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

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

Число обработанных элементов
Норма

Счётчик итераций равен Количество() массива

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

Счётчик меньше — часть элементов пропущена или цикл прерван раньше времени

Размер массива после удалений
Норма

Исходный размер минус число удалённых элементов

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

Размер не сходится — ошибка в логике индексов

Изменённые значения
Норма

Элементы изменились при обращении по индексу

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

Значения прежние — менялась переменная цикла, а не элемент

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

Можно ли обойти массив в обратном порядке без удаления?

Да. Вычислите количество элементов в переменную до цикла и идите от последнего индекса к нулю: «Для Счетчик = 1 По КоличествоЭлементов Цикл» с обращением «Массив[КоличествоЭлементов - Счетчик]». Это безопасно и для чтения, и для удаления.

Чем отличается «Найти» от ручного цикла поиска?

Метод «Найти» ищет точное совпадение значения и возвращает индекс или «Неопределено». Ручной цикл нужен, когда условие сложнее: сравнение по части строки, по нескольким полям структуры или с приведением типов.

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

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

Почему изменение элемента в «Для Каждого» не срабатывает?

Переменная цикла — копия ссылки на значение. Присваивание ей нового значения массив не меняет. Изменяйте элемент через «Массив[Индекс] = …» в цикле «Для» или, если элемент — объект, меняйте его свойства, а не саму переменную.

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

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

Как пропустить часть элементов при обходе?

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

Итог

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

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