gifts2017

Заполнение дерева из табличных данных без рекурсии

Опубликовал Makc *** (makc2k) в раздел Программирование - Универсальные функции

Предлагаю вашему вниманию альтернативный алгоритм построения дерева из данных документа или регистра. Этот способ позволяет без рекурсии построить иерархическую структуру данных. Для правильной работы у элемента данных должен быть ключ связи (родитель) и идентификатор элемента с где каталоги (узлы) младше элементов (файлов).

Идея заложена в неком контейнере, выпрямителе лесинки иерархических данных в двумерный массив. В моём варианте этим контейнером служит соответствие. Создаем контейнер, извлекаем данне запросом, сортируя по идентификатору. Если данные собирались в таблицу с помощью рекурсивной процедуры/функции, то узлы иерархии создаются раньше элемента, соответственно идентификатор элемента всегда старше родительского узла.

Дальше начинаем создавать дерево заполняя его свойство строки, новыми строками и параллельно вносим узлы в соответствие. Таким образом когда мы находимся на очередном элементе в соответствии можно найти родительскую строку дерева. В итоге выстраивается дерево.

// преобразует таблицу в дерево значений с помощью запроса (без рекурсии)
//
// Параметры
//  Документ  - ДокументСсылка - ссылка на документ табличную часть которого
//                 нужно преобразовать
//  ИмяТабЧасти  - Строка - Имя табличной части документа
//                 
//  ИмяПоляСортировки  - Строка - Имя поля табличной части документа по которому нужно сортировать данные
//                                      сортировка нужна для того чтобы данные перебирались от корня дерева 
//
//  ИмяПризнакаКаталога  - Строка - Имя поля табличной части документа в котором хранится флаг каталога (узла)
//
//  ИмяРодительскгоПоля  - Строка - Имя поля табличной части документа в котором хранится номер радительского каталога (узла)
//
// Возвращаемое значение:
//   ДервеоЗначений   - результат преобразования
//
Функция ЗапросТаблицыПреобразованиеВДерево(     Документ, 
                                                                                        ИмяТабЧасти, 
                                                                                        ИмяПоляСортировки, 
                                                                                        ИмяПризнакаКаталога, 
                                                                                        ИмяРодительскгоПоля) Экспорт
        //создаем соответствие куда будем складывать все строки признаком узла (ЭтоКаталог = Истина)
        КонвертДляСтрок = Новый Соответствие;
        
        //Создаем дерево и наполняем колонками
        Древо = Новый ДеревоЗначений;
        Для Каждого РеквизитТабЧасти Из Документ.Метаданные().ТабличныеЧасти[ИмяТабЧасти].Реквизиты Цикл
                Древо.Колонки.Добавить(РеквизитТабЧасти.Имя, РеквизитТабЧасти.Тип);     
        КонецЦикла;
        
    //извлекаем данные из табличной части документа
        //сортировка задается по номеру строки, подразумевается, что узлы имеют более младший номер
        //по сравнению с элементами. Именно в таком порядке рекурсивный опрос создает дерево, а мы работаем
        //уже с готовым деревом
        Запрос = Новый Запрос;
        Запрос.Текст = 
                "ВЫБРАТЬ
                |       *
                |ИЗ
                |       &Документ КАК Источник
                |ГДЕ
                |       Источник.Ссылка = &Ссылка
                |
                |УПОРЯДОЧИТЬ ПО
                |       Источник." + ИмяПоляСортировки;

        Запрос.УстановитьПараметр("Ссылка", Документ.Ссылка);
        Запрос.Текст = СтрЗаменить(Запрос.Текст, "&Документ", "Документ." + Документ.Метаданные().Имя + "." + ИмяТабЧасти);
    
        Результат = Запрос.Выполнить();

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

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

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Яков Коган (Yashazz) 05.08.14 10:11
1. Зачем такой изврат, была конкретная нужда?
2. Учите русский, ибо "лесИнка" это перебор;
3. Круче Ильдаровича в этой теме никого нету.
yandextesting; +1 Ответить 2
2. Makc *** (makc2k) 06.08.14 00:01
(1) Yashazz, ЛесИнка потому что лесина - лесить жердины (по малоросски, а по белорусски дробына соответственно дробить), а если серьезно, то не хочу модераторов беспокоить пустяком. Данный алгоритм придумал сам без чьей либо помощи, а статья для закрепления в памяти.
ben19791010; +1 Ответить 1
3. Борис Скворцов (gaglo) 06.08.14 08:11
А можно все-таки поподробнее, что это за документ, у которого в табличной части дерево, откуда оно туда попало и т.п.?
4. Максим Кузнецов (Makushimo) 06.08.14 08:39
(1) Yashazz,
Ildarovich крут, не спорю

но это, однако же, не бред
забираю, спасибо автору
5. DAnry (DAnry) 06.08.14 15:37
(2) makc2k, что такое "по малоросски", это диалект какой-то? Может вы имели ввиду все же "по украински".
6. Makc *** (makc2k) 06.08.14 21:42
Кому как приятнее. Я бы сказал на одном из диалектов группы славянских языков.

Аз - человек воплощенный на земле (в Сербском так и пишут АЗ - я), бога ведает, бога ведая, глаголит добро, есть живой .... Ну и так далее по азбуке.

Дерево в документ я соорудил сам, это для удобства просмотра.
7. Александр Полтава (Патриот) 30.09.14 15:13
А "радительского" это от слова "ра", что по Задорновски означало "Солнце" у древних славян? =)
8. Makc *** (makc2k) 30.09.14 23:29
Родительского - от слова род, а радость - это ра в достатке. Задорнов не икона, не он это придумал. Все это еще 100 лет назад употребимо было.