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

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

Как перебрать значения массива в 1С

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

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

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

Как устроен массив в 1С и что нужно знать перед перебором

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

Элементами массива могут быть значения любых типов, в том числе смешанные: числа, строки, ссылки на документы, структуры и даже другие массивы. Перед перебором полезно убедиться, что переменная действительно содержит массив, — например, функцией ТипЗнч(Переменная) = Тип("Массив"), если переменная приходит из внешнего источника.

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

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

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

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

КоличествоЭлементов = Товары.Количество(); // 3

ПервыйЭлемент = Товары[0]; // "Номенклатура 1"

ПоследнийЭлемент = Товары[Товары.Количество() - 1];

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

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

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

Сообщить("Обрабатывается: " + Товар);

КонецЦикла;

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

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

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

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

КонецЕсли;

Если Товар = "Номенклатура 2" Тогда

Сообщить("Найдено!");

Прервать; // дальше искать не нужно

КонецЕсли;

КонецЦикла;

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

Перебор циклом «Для» со счётчиком — когда нужен индекс

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

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

Сообщить("Позиция " + Индекс + ": " + Товары[Индекс]);

КонецЦикла;

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

Всего = Товары.Количество();

Для Индекс = 0 По Всего - 1 Цикл

Товары[Индекс] = СокрЛП(Товары[Индекс]); // изменение по индексу

КонецЦикла;

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

Для Каждого

Подходит для простой обработки всех элементов. Код короче и безопаснее, но нет доступа к индексу и нельзя менять состав коллекции внутри цикла.

Для со счётчиком

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

Обход с конца

Обязателен при удалении элементов внутри цикла: счётчик идёт от Количество()-1 до 0 с шагом -1.

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

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

Правильный приём — обход массива с конца. Удаление элементов с большими индексами не влияет на позиции ещё не обработанных элементов с меньшими индексами. Шаг цикла задаётся отрицательным: Для Индекс = Всего - 1 По 0 Цикл с конструкцией изменения шага через Цикл не поддерживается напрямую — в 1С шаг задаётся только как +1, поэтому используют форму с вычитанием или цикл Пока.

// Вариант 1: через вычисление индекса

Всего = Товары.Количество();

Для Сч = 1 По Всего Цикл

Индекс = Всего - Сч; // идём от последнего к первому

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

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

КонецЕсли;

КонецЦикла;

// Вариант 2: через цикл Пока

Индекс = Товары.Количество() - 1;

Пока Индекс >= 0 Цикл

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

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

КонецЕсли;

Индекс = Индекс - 1;

КонецЦикла;

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

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

ТаблицаДанных = Новый Массив;

Строка1 = Новый Массив;

Строка1.Добавить("Товар А");

Строка1.Добавить(10);

ТаблицаДанных.Добавить(Строка1);

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

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

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

КонецЦикла;

КонецЦикла;

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

Альтернативы ручному перебору

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

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

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

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

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

Иначе

Сообщить("Значение в массиве отсутствует");

КонецЕсли;

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

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

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

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

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

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

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

Цикл выполняет ноль итераций, ошибок нет

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

Ошибка обращения по индексу до или после цикла

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

Совпадает с Массив.Количество() до обработки

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

Обработано меньше — признак пропусков при удалении в цикле

Последний элемент
Норма

Обрабатывается, обращение по индексу Количество()-1

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

Выход за границы массива на последней итерации

Если внутри перебора массива выполняются запросы к базе данных, вынесите их из цикла: соберите нужные значения в массив или список значений и выполните один запрос с условием «В». Это сокращает число обращений к СУБД с сотен до одного и заметно ускоряет код.
ПЗ
Практическое замечание

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

Как перебрать массив в обратном порядке?

Используйте цикл Для Сч = 1 По Всего Цикл с вычислением индекса Индекс = Всего - Сч либо цикл Пока Индекс >= 0 с ручным уменьшением счётчика. Прямого отрицательного шага у цикла Для во встроенном языке нет.

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

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

Как удалить из массива все элементы, удовлетворяющие условию?

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

Как узнать, есть ли значение в массиве, без цикла?

Вызовите метод Массив.Найти(Значение): он вернёт индекс первого совпадения или Неопределено, если значение не найдено. Сравнение выполняется по значению, для ссылочных типов — по ссылке.

Можно ли перебирать массив, элементы которого имеют разные типы?

Да, массив в 1С не типизирован. Внутри цикла проверяйте тип каждого элемента через ТипЗнч(Элемент) перед операциями, специфичными для конкретного типа, иначе получите ошибку приведения или вызова несуществующего метода.

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

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

Итог

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

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