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

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

Как перебрать массив в 1С: способы, примеры и типичные ошибки

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

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

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

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

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

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

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

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

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

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

КонецЦикла;

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

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

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

Если ПустаяСтрока(Строка) Тогда

Продолжить; // пропускаем пустые

КонецЕсли;

Если Строка = ИскомоеЗначение Тогда

Найдено = Истина;

Прервать;

КонецЕсли;

КонецЦикла;

Перебор по индексу циклом «Для … По … Цикл»

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

Для Сч = 0 По Массив.Количество() - 1 Цикл

Массив[Сч] = СокрЛП(Массив[Сч]); // изменение элемента по месту

КонецЦикла;

Обращение по индексу выполняется через квадратные скобки: Массив[Сч]. Попытка обратиться к индексу вне диапазона вызовет ошибку «Индекс находится за границами массива», поэтому верхняя граница цикла всегда должна быть Количество() - 1, а не Количество().

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

ВерхняяГраница = Массив.Количество() - 1;

Для Сч = 0 По ВерхняяГраница Цикл

// обработка Массив[Сч]

КонецЦикла;

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

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

Сч = Массив.Количество() - 1;

Пока Сч >= 0 Цикл

Сообщить(Массив[Сч]);

Сч = Сч - 1;

КонецЦикла;

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

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

Для Сч = 1 По Колво Цикл

Элемент = Массив[Колво - Сч]; // от последнего к первому

КонецЦикла;

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

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

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

Сч = Массив.Количество() - 1;

Пока Сч >= 0 Цикл

Если ПустаяСтрока(Массив[Сч]) Тогда

Массив.Удалить(Сч);

КонецЕсли;

Сч = Сч - 1;

КонецЦикла;

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

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

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

Если Не ПустаяСтрока(Элемент) Тогда

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

КонецЕсли;

КонецЦикла;

Поиск элемента без полного перебора

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

Индекс = Массив.Найти(ИскомоеЗначение);

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

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

Иначе

Сообщить("Элемент отсутствует");

КонецЕсли;

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

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

Для Каждого

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

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

Нужен индекс, изменение элементов по месту, сравнение соседей. Требует аккуратности с границей Количество()-1.

Обратный обход (Пока)

Удаление элементов внутри цикла, обработка с конца. Чуть более громоздкий код.

Метод Найти()

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

Типичные ошибки и как их проверить

Большинство дефектов при переборе массивов сводится к четырём ситуациям. Первая — выход за границу: цикл Для Сч = 0 По Массив.Количество() Цикл на последней итерации обращается к несуществующему индексу. Вторая — изменение коллекции внутри Для Каждого. Третья — ожидание, что присваивание переменной цикла изменит элемент массива примитивного типа. Четвёртая — перебор пустого массива с обращением к Массив[0] до проверки Количество().

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

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

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

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

Совпадает с размером массива или с ожидаемым числом после фильтрации

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

Меньше ожидаемого без причины — вероятен пропуск из-за удаления в цикле

Границы индексов
Норма

Обращения только в диапазоне 0 … Количество()-1

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

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

Поведение на пустом массиве
Норма

Цикл просто не выполняется, код после него работает

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

Обращение к элементу до проверки Количество()

Практические сценарии

Перебор массива структур

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

Для Каждого Запись Из МассивЗаписей Цикл

Если Запись.Свойство("Сумма") И Запись.Сумма > 0 Тогда

Итог = Итог + Запись.Сумма;

КонецЕсли;

КонецЦикла;

Вложенный перебор двух массивов

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

СоответствиеЦен = Новый Соответствие;

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

СоответствиеЦен.Вставить(СтрокаЦены.Номенклатура, СтрокаЦены.Цена);

КонецЦикла;

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

Цена = СоответствиеЦен[Товар];

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

// обработка

КонецЕсли;

КонецЦикла;

Перебор массива, полученного из запроса

Метод Выгрузить() результата запроса возвращает таблицу значений, а не массив, но типовой приём — выгрузить одну колонку в массив: МассивСсылок = Результат.Выгрузить().ВыгрузитьКолонку("Ссылка"). Дальше такой массив перебирается любым из описанных способов. Если из результата запроса нужны несколько полей, перебирайте выборку или таблицу значений напрямую, не преобразуя в массив.

Итог

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

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

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

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

Напрямую — нет, если элемент является примитивным значением: переменная цикла получает копию значения. Присваивание переменной не изменит массив. Изменяйте элемент по индексу через цикл со счётчиком: Массив[Сч] = НовоеЗначение. Если элемент — объект (структура, строка таблицы значений), изменение его свойств через переменную цикла работает, так как передаётся ссылка.

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

Обращение выполняется к индексу, которого нет: отрицательному или большему, чем Количество() - 1. Чаще всего причина — цикл с верхней границей Количество() вместо Количество() - 1 либо обращение к первому элементу пустого массива. Проверяйте границу цикла и выполняйте проверку Массив.Количество() > 0 перед доступом по индексу.

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

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

Чем «Для Каждого» отличается от цикла со счётчиком по производительности?

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

Как найти в массиве все вхождения значения, а не только первое?

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

Работает ли перебор «Для Каждого» для фиксированного массива?

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