Разработка и Конфигурация · 10 мин
Как обойти структуру в цикле в 1С
Как обойти структуру в 1С циклом Для Каждого, получить ключи и значения, проверить наличие свойства, скопировать и сравнить структуры. Примеры кода и типичные ошибки.
Структура в языке 1С — это коллекция пар «ключ — значение», где ключ всегда строка, а значение может быть любого типа: числом, датой, ссылкой, другой структурой. Разработчики используют структуры для передачи параметров между процедурами, хранения настроек, подготовки данных для запросов и обмена. Поскольку у структуры нет индексов, как у массива, обходить её можно только через перебор элементов — и у этого перебора есть особенности, которые важно понимать.
Основной инструмент обхода — цикл «Для Каждого … Из … Цикл». На каждой итерации переменная цикла получает объект типа «КлючИЗначение», у которого доступны свойства «Ключ» и «Значение». Порядок обхода соответствует порядку добавления свойств в структуру, но полагаться на него в бизнес-логике не стоит — это внутренняя деталь реализации, а не гарантированный контракт.
В этой статье — базовый синтаксис обхода, безопасное изменение структуры внутри цикла, рекурсивный обход вложенных структур, копирование и сравнение, а также типичные ошибки, которые приводят к исключениям и потере данных.
Базовый синтаксис обхода структуры
Минимальный пример обхода выглядит так: создаётся структура, затем цикл перебирает все её элементы. Переменная цикла — это не значение свойства, а объект «КлючИЗначение», поэтому обращение идёт через точку.
Настройки = Новый Структура;
Настройки.Вставить("Организация", "ООО Ромашка");
Настройки.Вставить("Период", ТекущаяДата());
Настройки.Вставить("ВыводитьИтоги", Истина);
Для Каждого Элемент Из Настройки Цикл
Сообщить(Элемент.Ключ + " = " + Элемент.Значение);
КонецЦикла;
Обратите внимание: значение может быть любого типа, поэтому при выводе или конкатенации строк платформа выполняет неявное преобразование. Если значением окажется другая структура или массив, в строку оно преобразуется в служебное представление, а не в читаемый текст. Для сложных типов нужна отдельная обработка — проверка типа через ТипЗнч() и соответствующая ветка логики.
Если нужен только список ключей без значений, удобнее собрать их в строку или массив внутри того же цикла. Отдельного метода «ПолучитьКлючи» у структуры нет — перебор остаётся единственным способом узнать состав свойств.
Проверка наличия свойства и безопасное чтение
Обращение к несуществующему свойству структуры вызывает исключение «Поле объекта не обнаружено». Поэтому перед чтением значения, которого может не быть, используйте метод Свойство(). Он принимает имя ключа и, при необходимости, переменную, в которую будет помещено найденное значение.
ЗначениеПериод = Неопределено;
Если Настройки.Свойство("Период", ЗначениеПериод) Тогда
// свойство есть, значение в переменной ЗначениеПериод
Иначе
// свойства нет — используем значение по умолчанию
ЗначениеПериод = ТекущаяДата();
КонецЕсли;
Внутри цикла обхода проверка Свойство() обычно не нужна — все перебираемые ключи гарантированно существуют. Но она становится критичной, когда вы обходите одну структуру и читаете соответствующие ключи из другой, например при слиянии настроек или сопоставлении данных из двух источников.
Изменение структуры во время обхода: что можно и что нельзя
Частая задача — пройти по структуре и что-то в ней поменять: удалить пустые значения, переименовать ключи, добавить вычисляемые поля. Здесь кроется главная ловушка: изменять состав коллекции (добавлять или удалять свойства) во время её обхода нельзя — платформа выбросит исключение о недопустимой операции с коллекцией.
При этом изменять значение существующего свойства через прямое обращение по ключу допустимо, если не меняется состав коллекции. Но безопасный и универсальный шаблон выглядит иначе: сначала собираем планируемые изменения во временную структуру или массив, затем применяем их после цикла.
// Удаляем все свойства со значением Неопределено
КлючиКУдалению = Новый Массив;
Для Каждого Элемент Из Настройки Цикл
Если Элемент.Значение = Неопределено Тогда
КлючиКУдалению.Добавить(Элемент.Ключ);
КонецЕсли;
КонецЦикла;
Для Каждого Ключ Из КлючиКУдалению Цикл
Настройки.Удалить(Ключ);
КонецЦикла;
- 1Пройдите структуру циклом и запишите ключи для удаления или пары для добавления во временную коллекцию
- 2Завершите цикл обхода
- 3Примените изменения отдельным циклом по временной коллекции
- 4Проверьте результат повторным обходом или отладчиком
Обход вложенных структур и рекурсия
На практике структуры часто бывают многоуровневыми: настройки отчёта, данные из JSON, параметры обмена. Для обхода такого дерева используют рекурсивную процедуру: если значение элемента — структура или соответствие, процедура вызывает сама себя для этого значения.
Процедура ОбойтиРекурсивно(Коллекция, Путь = "")
Для Каждого Элемент Из Коллекция Цикл
ТекущийПуть = ?(Путь = "", Элемент.Ключ, Путь + "." + Элемент.Ключ);
Если ТипЗнч(Элемент.Значение) = Тип("Структура")
Или ТипЗнч(Элемент.Значение) = Тип("Соответствие") Тогда
ОбойтиРекурсивно(Элемент.Значение, ТекущийПуть);
Иначе
Сообщить(ТекущийПуть + " = " + Элемент.Значение);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Накопление полного пути к свойству («Настройки.Отбор.Организация») полезно при поиске значений, логировании и построении плоского представления дерева. Учитывайте ограничение: глубина рекурсии в 1С ограничена стеком вызовов, поэтому для данных с потенциально очень глубокой вложенностью лучше использовать явный стек на массиве вместо рекурсии.
Копирование, слияние и сравнение структур
Присваивание структуры другой переменной не создаёт копию — обе переменные ссылаются на один и тот же объект. Изменение через одну переменную будет видно через другую. Чтобы получить независимую копию, создайте новую структуру и перенесите элементы циклом.
Функция СкопироватьСтруктуру(Источник)
Копия = Новый Структура;
Для Каждого Элемент Из Источник Цикл
Копия.Вставить(Элемент.Ключ, Элемент.Значение);
КонецЦикла;
Возврат Копия;
КонецФункции
Такая копия поверхностная: если значениями являются вложенные структуры или массивы, скопируются ссылки на них. Для глубокого копирования нужна рекурсивная версия функции с проверкой типов. Слияние двух структур делается аналогично — обходом второй структуры с вставкой или перезаписью ключей в первую, при этом заранее решите, что делать при совпадении ключей: перезаписывать, сохранять исходное или объединять значения.
Сравнение двух структур также выполняется обходом: перебираете первую структуру, для каждого ключа проверяете наличие во второй через Свойство() и равенство значений, затем повторяете в обратную сторону для поиска ключей, которых нет в первой. Готового метода сравнения у платформы нет.
Структура или соответствие
Ключи — допустимые идентификаторы, обращение возможно через точку (Структура.Ключ). Подходит для параметров и настроек с известным составом полей.
Ключи — любые строки и даже другие типы, обращение только через квадратные скобки. Подходит для произвольных справочников ключ-значение из внешних данных.
Производительность и ограничения
Обход структуры — быстрая операция, и для типовых объёмов (десятки и сотни свойств) вопрос производительности не стоит. Проблемы начинаются, когда обход выполняется внутри другого цикла, например при обработке большой таблицы значений, где для каждой строки строится и обходится структура. В таких случаях структуру лучше создать один раз до цикла и переиспользовать, очищая через повторное присваивание Новый Структура или перезапись значений.
Ещё одно ограничение: структура не сериализуется напрямую в XML или JSON средствами платформы без обхода — при записи в JSON вы фактически перебираете элементы и передаёте их записи. Метод ЗаписатьJSON умеет работать со структурами напрямую, но внутри всё равно выполняется тот же перебор, поэтому циклические ссылки (структура, содержащая саму себя через цепочку вложений) приведут к ошибке.
Проверка результата обхода
После написания цикла обхода убедитесь, что он делает именно то, что задумано. Самый надёжный способ — отладчик: поставьте точку останова внутри цикла и посмотрите содержимое переменной Элемент на каждой итерации. Альтернатива — временный вывод через Сообщить() с ключом и значением.
Контрольные признаки корректной работы: количество итераций равно количеству свойств структуры, все ключи выводятся без исключений, после изменений состав структуры соответствует ожиданиям. Если обход «пропускает» элементы — почти наверняка где-то внутри цикла изменяется состав коллекции.
Что важно запомнить
Обход структуры выполняется циклом «Для Каждого» через пару КлючИЗначение. Состав коллекции внутри цикла не изменяют — изменения применяют после обхода. Для необязательных ключей используют Свойство(), для копирования — явный перенос элементов, для вложенных данных — рекурсию с контролем глубины.
Частые вопросы
Можно ли обойти структуру по индексу, как массив?
Нет, у структуры нет числовых индексов и метода Получить(). Единственный способ перебора — цикл «Для Каждого». Если нужен доступ по позиции, соберите ключи в массив внутри цикла и работайте уже с массивом.
Гарантирован ли порядок обхода элементов структуры?
На практике элементы перебираются в порядке добавления, но это особенность реализации, а не документированная гарантия. Если порядок критичен для логики, храните ключи в массиве и обращайтесь к структуре по этим ключам.
Как узнать количество свойств структуры без цикла?
Метод Количество() возвращает число свойств структуры напрямую, без обхода: Настройки.Количество(). Цикл нужен только тогда, когда требуется доступ к самим ключам и значениям.
Чем отличается обход соответствия от обхода структуры?
Синтаксис цикла идентичен — та же пара КлючИЗначение. Различия в самой коллекции: у соответствия ключи могут быть любыми значениями, а обращение к элементам возможно только через квадратные скобки, а не через точку.
Как найти значение во вложенной структуре, не зная точного пути?
Используйте рекурсивный обход с накоплением пути: процедура проверяет тип каждого значения и спускается глубже при обнаружении структуры или соответствия. Найденные совпадения собирайте в массив или таблицу значений вместе с полными путями.
Почему при выводе значения структуры в сообщение я вижу служебный текст вместо данных?
Если значением является другая структура, массив или объект, неявное преобразование в строку даёт служебное представление типа. Нужно проверить тип через ТипЗнч() и для коллекций выполнить отдельный обход, а не полагаться на автоматическое преобразование.