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

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

1. Классический цикл «Для Каждого» — простой, но не всегда оптимальный

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

Основной синтаксис:

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

// Обработка Элемент.Значение

КонецЦикла;

Плюсы метода:

  • 🔹 Простота чтения кода — идеально для начинающих
  • 🔹 Автоматическое определение типа элементов (число, строка, объект)
  • 🔹 Возможность модификации элементов прямо в цикле (с оговорками)

Минусы и подводные камни:

  • 🚨 Копирование данных: на каждой итерации создаётся копия элемента, что замедляет работу с большими массивами (>10 000 элементов)
  • 🚨 Невозможно получить индекс текущего элемента без дополнительных ухищрений
  • 🚨 Проблемы с пустыми значениями: цикл пропускает элементы со значением Неопределено
💡

Если вам нужен индекс элемента в цикле "Для Каждого", используйте дополнительную переменную-счётчик, увеличивая её на 1 в каждой итерации.

Пример с обработкой справочника Номенклатура:

Номенклатура = Справочники.Номенклатура.Выбрать();

МассивНоменклатуры = Номенклатура.Выгрузить();

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

Если Товар.ЭтоГруппа() Тогда

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

КонецЕсли;

Сообщить(Товар.Наименование + " - " + Товар.Артикул);

КонецЦикла;

⚠️ Внимание: При модификации элементов внутри цикла Для Каждого изменения применяются к копии, а не к оригинальному массиву. Чтобы сохранить изменения, используйте конструкцию Массив[Индекс] = НовоеЗначение.

2. Индексный перебор: когда нужны номера элементов

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

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

ТекущийЭлемент = Массив[Инд];

// Обработка

КонецЦикла;

Ключевые преимущества:

  • 🔢 Точный контроль над позицией элемента (полезно для алгоритмов сортировки или поиска)
  • 🔄 Возможность изменять оригинальный массив без создания копий
  • На 15-30% быстрее чем Для Каждого при работе с большими данными (>50 000 элементов)

Пример с модификацией массива:

Цены = Новый Массив;

Цены.Добавить(100);

Цены.Добавить(200);

Цены.Добавить(150);

// Увеличиваем все цены на 10%

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

Цены[Инд] = Цены[Инд] * 1.1;

КонецЦикла;

Обратите внимание на метод ВГраница() — он возвращает максимальный индекс массива (на 1 меньше реального количества элементов). Это частая причина ошибок у новичков!

Что будет если использовать Массив.Количество() вместо ВГраница()?

Метод Количество() возвращает фактическое число элементов (например, 3), а индексация начинается с 0. Если в цикле указать "Для Инд = 0 По Массив.Количество()", произойдёт выход за границы массива и ошибка "Индекс находится вне границ массива".

Метод Скорость (100 000 элементов) Память Модификация оригинала
Для Каждого ~1.2 сек Высокая (копии) Нет
Индексный цикл ~0.8 сек Низкая Да
Пока с счётчиком ~0.9 сек Низкая Да

3. Перебор ассоциативных массивов (Соответствие)

Для работы с парами "ключ-значение" в 1С используется объект Соответствие. Его перебор имеет свои особенности:

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

Данные.Вставить("Иванов", 1000);

Данные.Вставить("Петров", 1500);

Для Каждого Пара Из Данные Цикл

Сообщить(Пара.Ключ + ": " + Пара.Значение);

КонецЦикла;

Важные нюансы:

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

Пример с обработкой данных из Регистра сведений:

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ

Номенклатура КАК Ключ,

Цена КАК Значение

ИЗ РегистрСведений.ЦеныНоменклатуры

ГДЕ Период = &Дата";

Результат = Запрос.Выполнить(Новый Структура("Дата", ТекущаяДата()));

Выборка = Результат.Выбрать();

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

Пока Выборка.Следующий() Цикл

Цены.Вставить(Выборка.Ключ, Выборка.Значение);

КонецЦикла;

⚠️ Внимание: При добавлении элементов в Соответствие с одинаковыми ключами последние значения заменяют предыдущие. Это может привести к потере данных, если не контролировать уникальность ключей.
📊 Какой тип массива вы используете чаще в 1С?
Обычный Массив
Соответствие
СписокЗначений
Структура
Другой

4. Использование функций высшего порядка: Карта, Фильтр, Сократить

С версии 1С:Предприятие 8.3.14 появились встроенные функции для работы с массивами, аналогичные языкам вроде JavaScript. Они позволяют писать более лаконичный и безопасный код:

1. Карта(Массив, Функция) — применяет функцию ко всем элементам:

Числа = Новый Массив;

Числа.Добавить(1);

Числа.Добавить(2);

Числа.Добавить(3);

Удвоенные = Числа.Карта(Функция(Значение) Возврат Значение * 2 КонецФункции);

// Результат: [2, 4, 6]

2. Фильтр(Массив, Функция) — возвращает элементы, удовлетворяющие условию:

Положительные = Числа.Фильтр(Функция(Значение) Возврат Значение > 0 КонецФункции);

3. Сократить(Массив, Функция, НачальноеЗначение) — свёртка массива в одно значение:

Сумма = Числа.Сократить(

Функция(Накопленное, Текущее) Возврат Накопленное + Текущее КонецФункции,

0

);

// Результат: 6

Преимущества функционального подхода:

  • 📜 Декларативный стиль — код легче читать и поддерживать
  • 🛡️ Безопасность: функции не модифицируют оригинальный массив
  • 🔄 Возможность цепочечного вызова (chaining)

Пример цепочки обработки:

Результат = Данные

.Фильтр(Функция(Эл) Возврат Эл.Активный КонецФункции)

.Карта(Функция(Эл) Возврат Эл.Наименование КонецФункции)

.Сократить(Функция(Стр, Эл) Возврат Стр + Эл + ", " КонецФункции, "");

⚠️ Внимание: Функции Карта/Фильтр создают новые массивы, что может привести к повышенному потреблению памяти при работе с большими наборами данных (>100 000 элементов).

5. Оптимизация перебора для больших массивов

При обработке массивов с более 50 000 элементов стандартные методы могут работать медленно. Рассмотрим техники оптимизации:

1. Используйте Пока вместо Для:

Индекс = 0;

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

Элемент = Массив[Индекс];

// Обработка

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

КонецЦикла;

2. Разбивайте массив на части:

РазмерЧасти = 10000;

Для Смещение = 0 По Массив.ВГраница() Шаг РазмерЧасти Цикл

Часть = Массив.Скопировать(Смещение, РазмерЧасти);

ОбработатьЧасть(Часть); // Вынесите логику в отдельную процедуру

КонецЦикла;

3. Отключайте проверку границ (для опытных разработчиков):

// ТОЛЬКО если вы уверены в корректности индексов!

НастройкаБезопасности(Ложь);

Для Инд = 0 По МаксИндекс Цикл

Элемент = Массив[Инд]; // Без проверки выхода за границы

КонецЦикла;

НастройкаБезопасности(Истина);

Использование НастройкаБезопасности(Ложь) ускоряет доступ к элементам массива на 20-40%, но при ошибке в индексе приведёт к критическим сбоям платформы. Применяйте только в высоконагруженных расчётах с предварительной валидацией данных.

Использовать цикл Пока вместо Для Каждого|

Разбивать данные на части по 5 000-10 000 элементов|

Выносить обработку в отдельные процедуры|

Отключать проверку границ только при 100% уверенности в индексах|

Тестировать производительность на реальных данных-->

6. Типичные ошибки и как их избежать

Даже опытные разработчики допускают ошибки при работе с массивами. Разберём самые распространённые:

1. Модификация массива во время перебора:

// ОШИБКА: удаление элементов в цикле Для Каждого

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

Если Эл = Неопределено Тогда

Массив.Удалить(Эл); // Приведёт к исключению!

КонецЕсли;

КонецЦикла;

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

2. Путаница с индексацией:

// ОШИБКА: использование Количество() вместо ВГраница()

Для i = 1 По Массив.Количество() Цикл // Начнётся с 1, а не с 0!

...

КонецЦикла;

3. Работа с пустыми значениями:

Массив.Добавить(Неопределено);

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

Сообщить(Эл); // Элемент с Неопределено будет пропущен!

КонецЦикла;

4. Копирование объектов:

Объекты = Новый Массив;

Объекты.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Товар1"));

Для Каждого Объект Из Объекты Цикл

Объект.Наименование = "Новое имя"; // Изменится КОПИЯ, не оригинал!

КонецЦикла;

💡

Всегда проверяйте массивы на пустоту перед перебором: используйте конструкцию Если Массив.Количество() > 0 Тогда, чтобы избежать ненужных итераций.

7. Сравнение методов: какой выбрать?

Выбор метода перебора зависит от задачи. Вот рекомендации:

Задача Рекомендуемый метод Причина
Простой обход без модификаций Для Каждого Читаемость и безопасность
Модификация элементов по индексу Индексный цикл Для Прямой доступ к оригиналу
Работа с парами ключ-значение Для Каждого для Соответствие Удобный доступ к .Ключ и .Значение
Фильтрация/преобразование Функции Карта/Фильтр Декларативный стиль
Обработка >50 000 элементов Цикл Пока с разбивкой Производительность

Для критических участков кода (например, массовая обработка документов) замеряйте производительность разных методов на своих данных. Используйте ПолучитьИмяВременногоФайла() для логгирования времени выполнения:

Начало = ТекущаяДата();

// Ваш код перебора

Конец = ТекущаяДата();

Сообщить("Время выполнения: " + (Конец - Начало));

FAQ: Ответы на частые вопросы

Можно ли прервать цикл перебора досрочно?

Да, используйте оператор Прервать для цикла Для Каждого или Пока:

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

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

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

Прервать;

КонецЕсли;

КонецЦикла;

Для индексного цикла Для оператор Прервать также работает.

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

Используйте индексный цикл с убывающим счётчиком:

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

Элемент = Массив[Инд];

// Обработка

КонецЦикла;

Для Для Каждого обратный перебор невозможен без предварительного разворота массива.

Чем отличается Массив от СпискаЗначений?

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

  • 📋 Отображение (.Отображение)
  • 🔍 Поиск по значению (.НайтиПоЗначению())
  • 🔄 Сортировку (.СортироватьПоЗначению())

Перебор СпискаЗначений аналогичен массиву, но с доступом к дополнительным свойствам элементов.

Как перебрать результат запроса без выгрузки в массив?

Используйте Выборка напрямую — это экономит память:

Запрос = Новый Запрос("ВЫБРАТЬ ... ");

Результат = Запрос.Выполнить();

Выборка = Результат.Выбрать();

Пока Выборка.Следующий() Цикл

// Работа с Выборка.Колонка1, Выборка.Колонка2

КонецЦикла;

Выгрузка в массив (.Выгрузить()) нужна только если требуется многократный обход данных.

Почему цикл Для Каждого пропускает элементы?

Это происходит в двух случаях:

  1. Элемент имеет значение Неопределено — цикл Для Каждого их игнорирует.
  2. Массив был модифицирован во время перебора (добавлены/удалены элементы).

Решение: используйте индексный цикл или предварительно очищайте массив от Неопределено.