Разработка и Конфигурация · 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");
Если Индекс <> Неопределено Тогда
Сообщить("Найдено на позиции: " + Индекс);
Иначе
Сообщить("Значение в массиве отсутствует");
КонецЕсли;
Проверка результата и отладка перебора
После написания цикла проверьте его на граничных случаях: пустой массив, массив из одного элемента, массив с повторяющимися значениями. Пустой массив не должен вызывать ошибку — оба вида циклов корректно выполнят ноль итераций, но обращение к Массив[0] до цикла уже упадёт с ошибкой.
Для отладки используйте конфигуратор: поставьте точку останова внутри цикла и смотрите значения переменной цикла и счётчика в табло. При обработке больших массивов вместо Сообщить() на каждой итерации выводите прогресс раз в несколько сотен итераций — иначе панель сообщений замедлит выполнение и переполнится.
Как проверить результат
Цикл выполняет ноль итераций, ошибок нет
Ошибка обращения по индексу до или после цикла
Совпадает с Массив.Количество() до обработки
Обработано меньше — признак пропусков при удалении в цикле
Обрабатывается, обращение по индексу Количество()-1
Выход за границы массива на последней итерации
Если внутри перебора массива выполняются запросы к базе данных, вынесите их из цикла: соберите нужные значения в массив или список значений и выполните один запрос с условием «В». Это сокращает число обращений к СУБД с сотен до одного и заметно ускоряет код.
Частые вопросы
Как перебрать массив в обратном порядке?
Используйте цикл Для Сч = 1 По Всего Цикл с вычислением индекса Индекс = Всего - Сч либо цикл Пока Индекс >= 0 с ручным уменьшением счётчика. Прямого отрицательного шага у цикла Для во встроенном языке нет.
Чем «Для Каждого» отличается от «Для» при переборе массива?
«Для Каждого» передаёт в переменную само значение элемента и не даёт доступа к индексу — подходит для чтения. «Для» со счётчиком работает через индексы, позволяет менять элементы по позиции и обходить массив в произвольном порядке, но требует аккуратности с границами.
Как удалить из массива все элементы, удовлетворяющие условию?
Обходите массив с конца и вызывайте Удалить(Индекс) для подходящих элементов — тогда сдвиг оставшихся значений не влияет на ещё не проверенные позиции. Альтернатива: сформировать новый массив только из нужных элементов и присвоить его исходной переменной.
Как узнать, есть ли значение в массиве, без цикла?
Вызовите метод Массив.Найти(Значение): он вернёт индекс первого совпадения или Неопределено, если значение не найдено. Сравнение выполняется по значению, для ссылочных типов — по ссылке.
Можно ли перебирать массив, элементы которого имеют разные типы?
Да, массив в 1С не типизирован. Внутри цикла проверяйте тип каждого элемента через ТипЗнч(Элемент) перед операциями, специфичными для конкретного типа, иначе получите ошибку приведения или вызова несуществующего метода.
Что быстрее для больших данных: массив или таблица значений?
Для простого последовательного обхода разница невелика. Но если нужны поиск, фильтрация, сортировка или группировка, таблица значений выигрывает за счёт встроенных методов и индексов, а код становится короче и надёжнее ручных циклов.
Что важно запомнить
Для чтения всех элементов используйте «Для Каждого», для работы с индексами — «Для» со счётчиком от 0 до Количество()-1. Удаляйте элементы только при обходе с конца или после цикла. Проверяйте код на пустом массиве и не выполняйте запросы к базе внутри цикла.