При работе с объектом ДеревоЗначений начинающие разработчики часто сталкиваются с ситуацией, когда стандартный цикл Для Каждого Строка Из ДеревоЗначений.Строки обходит только строки первого (верхнего) уровня иерархии. Если глубина вложенности подчиненных элементов заранее неизвестна, простой перебор коллекции Строки не позволяет добраться до вложенных веток.
Рассмотрим несколько эффективных способов решения этой задачи: от классической рекурсии до методов обхода без использования рекурсивных вызовов.
Наиболее универсальный вариант обхода дерева с произвольным количеством уровней вложенности — использование рекурсивной процедуры. Идея заключается в том, что процедура принимает коллекцию строк и в цикле вызывает сама себя для подчиненных строк каждого элемента.
Разберем пример реализации такой процедуры:
Процедура ОбойтиСтрокиДерева(КоллекцияСтрок)
Для Каждого ТекущаяСтрока Из КоллекцияСтрок Цикл
// Выполняем необходимые операции со строкой (умножение, сложение и т.д.)
ОбработатьСтроку(ТекущаяСтрока);
// Если у строки есть подчиненные элементы, вызываем процедуру рекурсивно
Если ТекущаяСтрока.Строки.Количество() > 0 Тогда
ОбойтиСтрокиДерева(ТекущаяСтрока.Строки);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Для запуска полного обхода достаточно передать в процедуру верхний уровень дерева:
ОбойтиСтрокиДерева(ДеревоЗначений.Строки);
Если требуется обработать абсолютно все строки дерева без учета уровня их вложенности, можно обойтись без рекурсии. Для этого проанализируем возможности метода НайтиСтроки().
Если передать в метод пустую структуру отбора и установить флаг поиска подчиненных в Истина, платформа вернет плоский одномерный массив, содержащий все строки всех уровней иерархии.
// Формируем пустую структуру отбора
Отбор = Новый Структура;
// Получаем массив всех строк дерева, включая все подчиненные уровни
МассивВсехСтрок = ДеревоЗначений.Строки.НайтиСтроки(Отбор, Истина);
// Перебираем полученный массив обычным циклом
Для Каждого СтрокаДерева Из МассивВсехСтрок Цикл
// Выполняем рассуждения или вычисления
ОбработатьСтроку(СтрокаДерева);
КонецЦикла;
Этот способ отличается простотой написания кода и высокой скоростью выполнения, однако следует учитывать, что порядок строк в массиве может не сохранять строгое иерархическое подчинение (сверху вниз по веткам).
Разберем вариант итеративного обхода с помощью вспомогательного массива (стека или очереди). Этот метод полезен, когда нужно избежать глубоких рекурсивных вызовов и контролировать порядок обработки веток.
МассивОбхода = Новый Массив;
// Помещаем строки первого уровня в стек
Для Каждого СтрокаВерхнегоУровня Из ДеревоЗначений.Строки Цикл
МассивОбхода.Добавить(СтрокаВерхнегоУровня);
КонецЦикла;
Пока МассивОбхода.Количество() > 0 Цикл
// Извлекаем последний элемент из массива
ИндексПоследнего = МассивОбхода.Количество() - 1;
ТекущаяСтрока = МассивОбхода[ИндексПоследнего];
МассивОбхода.Удалить(ИндексПоследнего);
// Выполняем обработку текущей строки
ОбработатьСтроку(ТекущаяСтрока);
// Добавляем подчиненные строки в стек для последующего обхода
Для Каждого ПодчиненнаяСтрока Из ТекущаяСтрока.Строки Цикл
МассивОбхода.Добавить(ПодчиненнаяСтрока);
КонецЦикла;
КонецЦикла;
Если исходные данные формируются запросом, выгружать их в ДеревоЗначений не всегда обязательно. Вычисления и итоги можно получить напрямую из результата запроса с помощью иерархической выборки.
РезультатЗапроса = Запрос.Выполнить();
ВыборкаВерхнегоУровня = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Пока ВыборкаВерхнегоУровня.Следующий() Цикл
// Обработка элементов верхнего уровня
ВыборкаВторогоУровня = ВыборкаВерхнегоУровня.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Пока ВыборкаВторогоУровня.Следующий() Цикл
// Обработка подчиненных элементов
КонецЦикла;
КонецЦикла;
ДеревоЗначений доступен только на сервере или в толстом клиенте. На тонком клиенте при работе с управляемой формой используется объект ДанныеФормыДерево, у элементов которого коллекция подчиненных строк вызывается через метод ПолучитьЭлементы(), а не свойство Строки.НайтиСтроки() или итеративный стек.