Примеры работы с деревом значений

Примеры работы с деревом значений на языке программирования 1С:Предприятие. Примеры позволяют быстро разобраться в вопросе и использовать код в своих разработках

Создание и заполнение дерева значений

// Создание дерева значений, добавление корневых и дочерних элементов
Процедура СозданиеДереваЗначений()

    // Создаем новое дерево значений
    Дерево = Новый ДеревоЗначений;
    
    // Добавляем колонки
    Дерево.Колонки.Добавить("Наименование");
    Дерево.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
    Дерево.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
    
    // Добавляем корневые элементы (уровень 0)
    Строка1 = Дерево.Строки.Добавить();
    Строка1.Наименование = "Электроника";
    Строка1.Количество = 0;
    Строка1.Сумма = 0;
    
    // Добавляем дочерние элементы к первой строке
    Дочерняя1 = Строка1.Строки.Добавить();
    Дочерняя1.Наименование = "Ноутбуки";
    Дочерняя1.Количество = 10;
    Дочерняя1.Сумма = 500000;
    
    Дочерняя2 = Строка1.Строки.Добавить();
    Дочерняя2.Наименование = "Мониторы";
    Дочерняя2.Количество = 5;
    Дочерняя2.Сумма = 75000;
    
    // Добавляем второй корневой элемент
    Строка2 = Дерево.Строки.Добавить();
    Строка2.Наименование = "Мебель";
    Строка2.Количество = 0;
    Строка2.Сумма = 0;
    
    Дочерняя3 = Строка2.Строки.Добавить();
    Дочерняя3.Наименование = "Столы";
    Дочерняя3.Количество = 8;
    Дочерняя3.Сумма = 80000;
    
    Дочерняя4 = Строка2.Строки.Добавить();
    Дочерняя4.Наименование = "Стулья";
    Дочерняя4.Количество = 20;
    Дочерняя4.Сумма = 60000;
    
    // Вывод дерева
    Сообщить("Структура дерева:");
    ВывестиДерево(Дерево, "");
    
КонецПроцедуры

Процедура ВывестиДерево(Дерево, Отступ)
    Для Каждого Строка Из Дерево.Строки Цикл
        Сообщить(Отступ + Строка.Наименование + " (кол-во: " + Строка.Количество + 
                 ", сумма: " + Строка.Сумма + ")");
        Если Строка.Строки.Количество() > 0 Тогда
            ВывестиПоддерево(Строка.Строки, Отступ + "  ");
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Процедура ВывестиПоддерево(Строки, Отступ)
    Для Каждого Строка Из Строки Цикл
        Сообщить(Отступ + Строка.Наименование + " (кол-во: " + Строка.Количество + 
                 ", сумма: " + Строка.Сумма + ")");
        Если Строка.Строки.Количество() > 0 Тогда
            ВывестиПоддерево(Строка.Строки, Отступ + "  ");
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Обход дерева значений (рекурсивный и итеративный)

// Различные способы обхода дерева значений
Процедура ОбходДерева()

    // Создаем тестовое дерево
    Дерево = СоздатьТестовоеДерево();
    
    // Способ 1: рекурсивный обход
    Сообщить("Рекурсивный обход:");
    РекурсивныйОбход(Дерево.Строки, "");
    
    // Способ 2: обход с использованием стека (без рекурсии)
    Сообщить("Обход с использованием стека:");
    ОбходЧерезСтек(Дерево);
    
    // Способ 3: обход с получением всех строк (плоский список)
    Сообщить("Получение всех строк в плоский список:");
    ВсеСтроки = ПолучитьВсеСтроки(Дерево);
    Для Каждого Строка Из ВсеСтроки Цикл
        Сообщить(Строка.Наименование + " (уровень: " + Строка.Уровень() + ")");
    КонецЦикла;
    
КонецПроцедуры

Функция СоздатьТестовоеДерево()
    Дерево = Новый ДеревоЗначений;
    Дерево.Колонки.Добавить("Наименование");
    Дерево.Колонки.Добавить("Код", Новый ОписаниеТипов("Число"));
    
    Корень1 = Дерево.Строки.Добавить();
    Корень1.Наименование = "Отдел 1";
    Корень1.Код = 1;
    
    Дочер1 = Корень1.Строки.Добавить();
    Дочер1.Наименование = "Группа 1.1";
    Дочер1.Код = 11;
    
    Внук1 = Дочер1.Строки.Добавить();
    Внук1.Наименование = "Сотрудник 1.1.1";
    Внук1.Код = 111;
    
    Корень2 = Дерево.Строки.Добавить();
    Корень2.Наименование = "Отдел 2";
    Корень2.Код = 2;
    
    Дочер2 = Корень2.Строки.Добавить();
    Дочер2.Наименование = "Группа 2.1";
    Дочер2.Код = 21;
    
    Возврат Дерево;
КонецФункции

Процедура РекурсивныйОбход(Строки, Отступ)
    Для Каждого Строка Из Строки Цикл
        Сообщить(Отступ + Строка.Наименование + " (код: " + Строка.Код + ")");
        РекурсивныйОбход(Строка.Строки, Отступ + "  ");
    КонецЦикла;
КонецПроцедуры

Процедура ОбходЧерезСтек(Дерево)
    Стек = Новый Массив;
    
    // Добавляем все корневые строки в стек в обратном порядке
    Для Инд = Дерево.Строки.Количество() - 1 По 0 Шаг -1 Цикл
        Стек.Добавить(Дерево.Строки[Инд]);
    КонецЦикла;
    
    Пока Стек.Количество() > 0 Цикл
        Текущий = Стек[Стек.Количество() - 1];
        Стек.Удалить(Стек.Количество() - 1);
        
        Сообщить(Текущий.Наименование + " (код: " + Текущий.Код + ")");
        
        // Добавляем дочерние элементы в стек
        Для Инд = Текущий.Строки.Количество() - 1 По 0 Шаг -1 Цикл
            Стек.Добавить(Текущий.Строки[Инд]);
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

Функция ПолучитьВсеСтроки(Дерево)
    Результат = Новый Массив;
    Строки = Дерево.Строки;
    ДобавитьСтрокиВМассив(Строки, Результат);
    Возврат Результат;
КонецФункции

Процедура ДобавитьСтрокиВМассив(Строки, Массив)
    Для Каждого Строка Из Строки Цикл
        Массив.Добавить(Строка);
        Если Строка.Строки.Количество() > 0 Тогда
            ДобавитьСтрокиВМассив(Строка.Строки, Массив);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Поиск элементов в дереве значений

// Поиск строк в дереве по различным критериям
Процедура ПоискВДереве()

    // Создаем дерево с данными
    Дерево = Новый ДеревоЗначений;
    Дерево.Колонки.Добавить("Наименование");
    Дерево.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
    Дерево.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
    
    Категория1 = Дерево.Строки.Добавить();
    Категория1.Наименование = "Электроника";
    
    Товар1 = Категория1.Строки.Добавить();
    Товар1.Наименование = "Ноутбук"; Товар1.Цена = 50000; Товар1.Количество = 10;
    
    Товар2 = Категория1.Строки.Добавить();
    Товар2.Наименование = "Монитор"; Товар2.Цена = 15000; Товар2.Количество = 5;
    
    Категория2 = Дерево.Строки.Добавить();
    Категория2.Наименование = "Мебель";
    
    Товар3 = Категория2.Строки.Добавить();
    Товар3.Наименование = "Стол"; Товар3.Цена = 8000; Товар3.Количество = 8;
    
    Товар4 = Категория2.Строки.Добавить();
    Товар4.Наименование = "Стул"; Товар4.Цена = 2500; Товар4.Количество = 20;
    
    // Поиск по наименованию
    НайденнаяСтрока = НайтиПоНаименованию(Дерево, "Монитор");
    Если НайденнаяСтрока <> Неопределено Тогда
        Сообщить("Найден товар: " + НайденнаяСтрока.Наименование + 
                 ", цена: " + НайденнаяСтрока.Цена);
    КонецЕсли;
    
    // Поиск всех товаров с ценой > 10000
    Результаты = НайтиПоУсловию(Дерево, "Цена > 10000");
    Сообщить("Товары с ценой более 10000:");
    Для Каждого Строка Из Результаты Цикл
        Сообщить("  " + Строка.Наименование + " - " + Строка.Цена);
    КонецЦикла;
    
    // Поиск по родителю
    Родитель = НайтиРодителя(Дерево, "Стул");
    Если Родитель <> Неопределено Тогда
        Сообщить("Родитель стула: " + Родитель.Наименование);
    КонецЕсли;
    
КонецПроцедуры

Функция НайтиПоНаименованию(Дерево, Наименование)
    Для Каждого Строка Из Дерево.Строки Цикл
        Если Строка.Наименование = Наименование Тогда
            Возврат Строка;
        КонецЕсли;
        
        Результат = НайтиСтрокуВВетке(Строка.Строки, Наименование);
        Если Результат <> Неопределено Тогда
            Возврат Результат;
        КонецЕсли;
    КонецЦикла;
    Возврат Неопределено;
КонецФункции

Функция НайтиСтрокуВВетке(Строки, Наименование)
    Для Каждого Строка Из Строки Цикл
        Если Строка.Наименование = Наименование Тогда
            Возврат Строка;
        КонецЕсли;
        
        Результат = НайтиСтрокуВВетке(Строка.Строки, Наименование);
        Если Результат <> Неопределено Тогда
            Возврат Результат;
        КонецЕсли;
    КонецЦикла;
    Возврат Неопределено;
КонецФункции

Функция НайтиПоУсловию(Дерево, УсловиеВыражение)
    Результат = Новый Массив;
    ДобавитьПодходящиеСтроки(Дерево.Строки, УсловиеВыражение, Результат);
    Возврат Результат;
КонецФункции

Процедура ДобавитьПодходящиеСтроки(Строки, УсловиеВыражение, Результат)
    Для Каждого Строка Из Строки Цикл
        // Упрощенная проверка (в реальном коде используйте Вычислить)
        Если Строка.Цена > 10000 Тогда
            Результат.Добавить(Строка);
        КонецЕсли;
        
        Если Строка.Строки.Количество() > 0 Тогда
            ДобавитьПодходящиеСтроки(Строка.Строки, УсловиеВыражение, Результат);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Функция НайтиРодителя(Дерево, ИмяРебенка)
    Для Каждого Строка Из Дерево.Строки Цикл
        Родитель = НайтиРодителяВВетке(Строка, ИмяРебенка);
        Если Родитель <> Неопределено Тогда
            Возврат Родитель;
        КонецЕсли;
    КонецЦикла;
    Возврат Неопределено;
КонецФункции

Функция НайтиРодителяВВетке(ПотенциальныйРодитель, ИмяРебенка)
    Для Каждого Ребенок Из ПотенциальныйРодитель.Строки Цикл
        Если Ребенок.Наименование = ИмяРебенка Тогда
            Возврат ПотенциальныйРодитель;
        КонецЕсли;
        
        Результат = НайтиРодителяВВетке(Ребенок, ИмяРебенка);
        Если Результат <> Неопределено Тогда
            Возврат Результат;
        КонецЕсли;
    КонецЦикла;
    Возврат Неопределено;
КонецФункции

Агрегация и подсчет итогов в дереве значений

// Расчет итоговых сумм и количества на уровнях дерева
Процедура ПодсчетИтоговВДереве()

    Дерево = Новый ДеревоЗначений;
    Дерево.Колонки.Добавить("Наименование");
    Дерево.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
    Дерево.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
    Дерево.Колонки.Добавить("ИтогКоличество", Новый ОписаниеТипов("Число"));
    Дерево.Колонки.Добавить("ИтогСумма", Новый ОписаниеТипов("Число"));
    
    // Заполняем дерево
    Филиал1 = Дерево.Строки.Добавить();
    Филиал1.Наименование = "Филиал Северный";
    
    Отдел1 = Филиал1.Строки.Добавить();
    Отдел1.Наименование = "Отдел продаж";
    Отдел1.Количество = 5;
    Отдел1.Сумма = 500000;
    
    Отдел2 = Филиал1.Строки.Добавить();
    Отдел2.Наименование = "Отдел закупок";
    Отдел2.Количество = 3;
    Отдел2.Сумма = 300000;
    
    Филиал2 = Дерево.Строки.Добавить();
    Филиал2.Наименование = "Филиал Южный";
    
    Отдел3 = Филиал2.Строки.Добавить();
    Отдел3.Наименование = "Отдел продаж";
    Отдел3.Количество = 8;
    Отдел3.Сумма = 800000;
    
    // Рассчитываем итоги
    РассчитатьИтогиДерева(Дерево);
    
    // Выводим дерево с итогами
    Сообщить("Дерево с рассчитанными итогами:");
    ВывестиДеревоСИтогами(Дерево.Строки, "");
    
КонецПроцедуры

Функция РассчитатьИтогиДерева(Дерево)
    РассчитатьИтогиВетки(Дерево.Строки);
КонецФункции

Функция РассчитатьИтогиВетки(Строки)
    Для Каждого Строка Из Строки Цикл
        Если Строка.Строки.Количество() > 0 Тогда
            // Рекурсивно рассчитываем итоги детей
            РассчитатьИтогиВетки(Строка.Строки);
            
            // Суммируем данные детей
            ИтогКол = 0;
            ИтогСум = 0;
            
            Для Каждого Ребенок Из Строка.Строки Цикл
                Если Ребенок.Строки.Количество() > 0 Тогда
                    ИтогКол = ИтогКол + Ребенок.ИтогКоличество;
                    ИтогСум = ИтогСум + Ребенок.ИтогСумма;
                Иначе
                    ИтогКол = ИтогКол + Ребенок.Количество;
                    ИтогСум = ИтогСум + Ребенок.Сумма;
                КонецЕсли;
            КонецЦикла;
            
            Строка.ИтогКоличество = ИтогКол;
            Строка.ИтогСумма = ИтогСум;
        Иначе
            // Для листовых узлов итог равен собственным значениям
            Строка.ИтогКоличество = Строка.Количество;
            Строка.ИтогСумма = Строка.Сумма;
        КонецЕсли;
    КонецЦикла;
КонецФункции

Процедура ВывестиДеревоСИтогами(Строки, Отступ)
    Для Каждого Строка Из Строки Цикл
        Сообщить(Отступ + Строка.Наименование + 
                 " (собственные: кол-во=" + Строка.Количество + 
                 ", сумма=" + Строка.Сумма + 
                 " | итого: кол-во=" + Строка.ИтогКоличество + 
                 ", сумма=" + Строка.ИтогСумма + ")");
        
        Если Строка.Строки.Количество() > 0 Тогда
            ВывестиДеревоСИтогами(Строка.Строки, Отступ + "  ");
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Загрузка данных в дерево из таблицы значений

// Преобразование плоской таблицы в иерархическое дерево
Процедура ТаблицаВДерево()

    // Исходная плоская таблица
    Таблица = Новый ТаблицаЗначений;
    Таблица.Колонки.Добавить("Код");
    Таблица.Колонки.Добавить("Родитель");
    Таблица.Колонки.Добавить("Наименование");
    Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
    
    Таблица.Добавить().Код = 1; Таблица[0].Родитель = 0; Таблица[0].Наименование = "Все товары"; Таблица[0].Количество = 0;
    Таблица.Добавить().Код = 2; Таблица[1].Родитель = 1; Таблица[1].Наименование = "Электроника"; Таблица[1].Количество = 0;
    Таблица.Добавить().Код = 3; Таблица[2].Родитель = 2; Таблица[2].Наименование = "Ноутбуки"; Таблица[2].Количество = 15;
    Таблица.Добавить().Код = 4; Таблица[3].Родитель = 2; Таблица[3].Наименование = "Мониторы"; Таблица[3].Количество = 8;
    Таблица.Добавить().Код = 5; Таблица[4].Родитель = 1; Таблица[4].Наименование = "Мебель"; Таблица[4].Количество = 0;
    Таблица.Добавить().Код = 6; Таблица[5].Родитель = 5; Таблица[5].Наименование = "Столы"; Таблица[5].Количество = 10;
    Таблица.Добавить().Код = 7; Таблица[6].Родитель = 5; Таблица[6].Наименование = "Стулья"; Таблица[6].Количество = 25;
    
    // Создаем дерево
    Дерево = Новый ДеревоЗначений;
    Дерево.Колонки.Добавить("Код", Новый ОписаниеТипов("Число"));
    Дерево.Колонки.Добавить("Наименование");
    Дерево.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
    
    // Создаем соответствие для быстрого поиска строк по коду
    ТаблицаКодировка = Новый Соответствие;
    Для Каждого СтрокаТаблицы Из Таблица Цикл
        ТаблицаКодировка.Вставить(СтрокаТаблицы.Код, СтрокаТаблицы);
    КонецЦикла;
    
    // Функция добавления строки в дерево по родителю
    Процедура ДобавитьСтрокуВДерево(РодительСтрока, СтрокаДанных, Дерево)
        НоваяСтрока = РодительСтрока.Строки.Добавить();
        НоваяСтрока.Код = СтрокаДанных.Код;
        НоваяСтрока.Наименование = СтрокаДанных.Наименование;
        НоваяСтрока.Количество = СтрокаДанных.Количество;
    КонецПроцедуры
    
    // Находим корневые элементы и строим дерево
    Для Каждого СтрокаТаблицы Из Таблица Цикл
        Если СтрокаТаблицы.Родитель = 0 Тогда
            // Корневой элемент
            Корень = Дерево.Строки.Добавить();
            Корень.Код = СтрокаТаблицы.Код;
            Корень.Наименование = СтрокаТаблицы.Наименование;
            Корень.Количество = СтрокаТаблицы.Количество;
        КонецЕсли;
    КонецЦикла;
    
    // Рекурсивное добавление дочерних элементов
    Для Каждого Корень Из Дерево.Строки Цикл
        ДобавитьДочерниеЭлементы(Корень, ТаблицаКодировка);
    КонецЦикла;
    
    // Вывод результата
    Сообщить("Дерево, построенное из таблицы:");
    ВывестиДеревоПоСтрокам(Дерево.Строки, "");
    
КонецПроцедуры

Процедура ДобавитьДочерниеЭлементы(Родитель, ТаблицаКодировка)
    Для Каждого КлючЗначение Из ТаблицаКодировка Цикл
        СтрокаДанных = КлючЗначение.Значение;
        Если СтрокаДанных.Родитель = Родитель.Код Тогда
            // Создаем дочерний элемент
            Ребенок = Родитель.Строки.Добавить();
            Ребенок.Код = СтрокаДанных.Код;
            Ребенок.Наименование = СтрокаДанных.Наименование;
            Ребенок.Количество = СтрокаДанных.Количество;
            
            // Рекурсивно добавляем внуков
            ДобавитьДочерниеЭлементы(Ребенок, ТаблицаКодировка);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Процедура ВывестиДеревоПоСтрокам(Строки, Отступ)
    Для Каждого Строка Из Строки Цикл
        Сообщить(Отступ + Строка.Наименование + " (код: " + Строка.Код + 
                 ", кол-во: " + Строка.Количество + ")");
        Если Строка.Строки.Количество() > 0 Тогда
            ВывестиДеревоПоСтрокам(Строка.Строки, Отступ + "  ");
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Сохранение и загрузка дерева значений

// Сохранение дерева в XML и восстановление
Процедура СохранениеИЗагрузкаДерева()

    // Создаем исходное дерево
    Исходное = Новый ДеревоЗначений;
    Исходное.Колонки.Добавить("Имя");
    Исходное.Колонки.Добавить("Возраст", Новый ОписаниеТипов("Число"));
    
    Корень = Исходное.Строки.Добавить();
    Корень.Имя = "Группа 1";
    Корень.Возраст = 0;
    
    Ребенок1 = Корень.Строки.Добавить();
    Ребенок1.Имя = "Иванов";
    Ребенок1.Возраст = 25;
    
    Ребенок2 = Корень.Строки.Добавить();
    Ребенок2.Имя = "Петров";
    Ребенок2.Возраст = 30;
    
    // Сохраняем в XML
    СтрокаXML = СохранитьДеревоВXML(Исходное);
    Сообщить("Сериализованное дерево:");
    Сообщить(Лев(СтрокаXML, 500) + "...");
    
    // Восстанавливаем из XML
    Восстановленное = ЗагрузитьДеревоXML(СтрокаXML);
    
    // Сравниваем
    Сообщить("Восстановленное дерево:");
    ВывестиДерево(Восстановленное, "");
    
    // Сравниваем идентичность
    Совпадают = СравнитьДеревья(Исходное, Восстановленное);
    Сообщить("Деревья совпадают: " + ?(Совпадают, "Да", "Нет"));
    
КонецПроцедуры

Функция СохранитьДеревоВXML(Дерево)
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    ЗаписьXML.ЗаписатьОбъявлениеXML();
    ЗаписьXML.ЗаписатьНачалоЭлемента("ДеревоЗначений");
    
    // Сохраняем структуру колонок
    ЗаписьXML.ЗаписатьНачалоЭлемента("Колонки");
    Для Каждого Колонка Из Дерево.Колонки Цикл
        ЗаписьXML.ЗаписатьНачалоЭлемента("Колонка");
        ЗаписьXML.ЗаписатьАтрибут("Имя", Колонка.Имя);
        ЗаписьXML.ЗаписатьАтрибут("Тип", Строка(Колонка.ТипЗначения));
        ЗаписьXML.ЗаписатьКонецЭлемента();
    КонецЦикла;
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // Сохраняем строки
    ЗаписьXML.ЗаписатьНачалоЭлемента("Строки");
    СохранитьВеткуВXML(Дерево.Строки, ЗаписьXML);
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    ЗаписьXML.ЗаписатьКонецЭлемента();
    Возврат ЗаписьXML.Закрыть();
КонецФункции

Процедура СохранитьВеткуВXML(Строки, ЗаписьXML)
    Для Каждого Строка Из Строки Цикл
        ЗаписьXML.ЗаписатьНачалоЭлемента("Строка");
        
        // Сохраняем значения колонок
        ЗаписьXML.ЗаписатьНачалоЭлемента("Значения");
        Для Каждого Колонка Из Строки.Владелец().Колонки Цикл
            ЗаписьXML.ЗаписатьНачалоЭлемента("Значение");
            ЗаписьXML.ЗаписатьАтрибут("Колонка", Колонка.Имя);
            ЗаписьXML.ЗаписатьТекст(Строка[Колонка.Имя]);
            ЗаписьXML.ЗаписатьКонецЭлемента();
        КонецЦикла;
        ЗаписьXML.ЗаписатьКонецЭлемента();
        
        // Сохраняем дочерние строки
        Если Строка.Строки.Количество() > 0 Тогда
            ЗаписьXML.ЗаписатьНачалоЭлемента("Дочерние");
            СохранитьВеткуВXML(Строка.Строки, ЗаписьXML);
            ЗаписьXML.ЗаписатьКонецЭлемента();
        КонецЕсли;
        
        ЗаписьXML.ЗаписатьКонецЭлемента();
    КонецЦикла;
КонецПроцедуры

Функция ЗагрузитьДеревоXML(СтрокаXML)
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(СтрокаXML);
    
    Дерево = Новый ДеревоЗначений;
    
    // Пропускаем объявление и корневой элемент
    ЧтениеXML.Прочитать();
    ЧтениеXML.Прочитать();
    
    // Читаем колонки
    ЧтениеXML.Прочитать();
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Колонки" Тогда
        ЧтениеXML.Прочитать();
        Пока ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Колонка" Цикл
            Имя = ЧтениеXML.ПолучитьАтрибут("Имя");
            ТипСтрока = ЧтениеXML.ПолучитьАтрибут("Тип");
            Дерево.Колонки.Добавить(Имя);
            ЧтениеXML.Прочитать();
            ЧтениеXML.Прочитать();
        КонецЦикла;
    КонецЕсли;
    
    // Читаем строки
    ЧтениеXML.Прочитать();
    Если ЧтениеXML.Имя = "Строки" Тогда
        ЧтениеXML.Прочитать();
        ЗагрузитьВеткуИзXML(Дерево.Строки, ЧтениеXML);
    КонецЕсли;
    
    Возврат Дерево;
КонецФункции

Процедура ЗагрузитьВеткуИзXML(СтрокиРодителя, ЧтениеXML)
    Пока ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Строка" Цикл
        НоваяСтрока = СтрокиРодителя.Добавить();
        
        // Читаем значения колонок
        ЧтениеXML.Прочитать();
        Если ЧтениеXML.Имя = "Значения" Тогда
            ЧтениеXML.Прочитать();
            Пока ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Значение" Цикл
                ИмяКолонки = ЧтениеXML.ПолучитьАтрибут("Колонка");
                ЧтениеXML.Прочитать();
                Значение = ЧтениеXML.Значение;
                НоваяСтрока[ИмяКолонки] = Значение;
                ЧтениеXML.Прочитать();
                ЧтениеXML.Прочитать();
            КонецЦикла;
        КонецЕсли;
        
        // Читаем дочерние строки
        ЧтениеXML.Прочитать();
        Если ЧтениеXML.Имя = "Дочерние" Тогда
            ЧтениеXML.Прочитать();
            ЗагрузитьВеткуИзXML(НоваяСтрока.Строки, ЧтениеXML);
            ЧтениеXML.Прочитать();
        КонецЕсли;
        
        ЧтениеXML.Прочитать();
    КонецЦикла;
КонецПроцедуры

Функция СравнитьДеревья(Дерево1, Дерево2)
    Если Дерево1.Колонки.Количество() <> Дерево2.Колонки.Количество() Тогда
        Возврат Ложь;
    КонецЕсли;
    
    Если Дерево1.Строки.Количество() <> Дерево2.Строки.Количество() Тогда
        Возврат Ложь;
    КонецЕсли;
    
    Для Инд = 0 По Дерево1.Строки.Количество() - 1 Цикл
        Если Не СравнитьВетки(Дерево1.Строки[Инд], Дерево2.Строки[Инд]) Тогда
            Возврат Ложь;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Истина;
КонецФункции

Функция СравнитьВетки(Строка1, Строка2)
    Для Каждого Колонка Из Строка1.Владелец().Колонки Цикл
        Если Строка1[Колонка.Имя] <> Строка2[Колонка.Имя] Тогда
            Возврат Ложь;
        КонецЕсли;
    КонецЦикла;
    
    Если Строка1.Строки.Количество() <> Строка2.Строки.Количество() Тогда
        Возврат Ложь;
    КонецЕсли;
    
    Для Инд = 0 По Строка1.Строки.Количество() - 1 Цикл
        Если Не СравнитьВетки(Строка1.Строки[Инд], Строка2.Строки[Инд]) Тогда
            Возврат Ложь;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Истина;
КонецФункции

Примечания

// Важные особенности работы с деревом значений:
// 1. Дерево значений предназначено для хранения иерархических данных
// 2. Каждая строка дерева имеет коллекцию Строки для доступа к дочерним элементам
// 3. Уровень вложенности строки можно получить методом Уровень()
// 4. Для обхода дерева используйте рекурсивные функции или стек
// 5. При удалении строки все ее дочерние элементы также удаляются
// 6. Дерево значений часто используется для представления отчетов с группировками
// 7. Для расчета итогов используйте рекурсивное суммирование по дочерним элементам
// 8. Поиск в дереве требует полного обхода, так как нет индексов
// 9. Дерево можно сериализовать в XML для сохранения и передачи
// 10. При загрузке из плоской таблицы нужно строить иерархию по полю-родителю
// 11. Для отображения дерева на форме используйте элемент управления "ДеревоЗначений"
// 12. Производительность дерева снижается при большом количестве вложенных уровней и строк

Поделиться с друзьями
Smirnov code
Добавить комментарий