Быстрая конвертация Таблицы значений в Дерево значений и обратно

03.06.25

Разработка - Универсальные функции

Появилась задача по изменению и доработке данных в "ДеревеЗначений". Причем конфигурация и оболочка достаточно древние, так что БСП я сразу отбросил. Ниже представлены функции по конвертации соответствующих объектов.

Сложно себе представить ситуацию, когда бы понадобились подобные функции, но и тем не менее такой момент настал. Встала необходимость анализа уже собранных данных из "Дерева значений", а так же их доработка. После доработки данные нужно обратно загрузить в Дерево. Вот такая специфическая задача.

Глянув в интернете не нашел достойной реализации по этому представляю свой вариант ниже. 

 

Метод получения Таблицы значений из Дерева значений



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

// Вспомогательная процедура для рекурсивного обхода дерева
Процедура ОбойтиСтрокиДерева(СтрокиДерева, ТаблицаЗначений, ИдентификаторРодителя, УровеньБаза, ДобавлятьКолонкуУровня)
    
    Для Каждого СтрокаДерева Из СтрокиДерева Цикл
        
        НоваяСтрока = ТаблицаЗначений.Добавить();
        ИдентификаторТекущейСтроки = Новый УникальныйИдентификатор;
        
        // Копируем данные из строки дерева
        Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
            Если Колонка.Имя <> "ИдентификаторСтроки" 
                И Колонка.Имя <> "ИдентификаторРодителя" 
                И Колонка.Имя <> "УровеньБаза" Тогда
                НоваяСтрока[Колонка.Имя] = СтрокаДерева[Колонка.Имя];
            КонецЕсли;
        КонецЦикла;
        
        // Заполняем служебные поля
        НоваяСтрока.ИдентификаторСтроки = ИдентификаторТекущейСтроки;
        НоваяСтрока.ИдентификаторРодителя = ИдентификаторРодителя;
        
        Если ДобавлятьКолонкуУровня Тогда
            НоваяСтрока.УровеньБаза = УровеньБаза;
        КонецЕсли;
        
        // Рекурсивно обрабатываем дочерние строки
        Если СтрокаДерева.Строки.Количество() > 0 Тогда
            ОбойтиСтрокиДерева(СтрокаДерева.Строки, ТаблицаЗначений, ИдентификаторТекущейСтроки, УровеньБаза + 1, ДобавлятьКолонкуУровня);
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

 

 

Метод получения  Дерева значений из Таблицы значений

 



// ИСПРАВЛЕННАЯ функция преобразования ТаблицаЗначений в ДеревоЗначений
Функция ТаблицуЗначенийВДеревоЗначений(ТаблицаЗначений) Экспорт
    
    ДеревоЗначений = Новый ДеревоЗначений;
    
    // Копируем колонки (исключая служебные)
    Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
        Если Колонка.Имя <> "ИдентификаторСтроки" 
            И Колонка.Имя <> "ИдентификаторРодителя" 
            И Колонка.Имя <> "УровеньБаза" Тогда
            НоваяКолонка = ДеревоЗначений.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения, Колонка.Заголовок);
            НоваяКолонка.Ширина = Колонка.Ширина;
        КонецЕсли;
    КонецЦикла;
    
    // Создаем соответствие для быстрого поиска строк по идентификатору
    СоответствиеСтрок = Новый Соответствие;
    
    // УБРАЛ сортировку - обрабатываем строки в исходном порядке!
    // Обрабатываем все строки в исходном порядке
    Для Каждого СтрокаТаблицы Из ТаблицаЗначений Цикл
        
        Если СтрокаТаблицы.ИдентификаторРодителя = Неопределено Тогда
            // Корневая строка
            НоваяСтрока = ДеревоЗначений.Строки.Добавить();
        Иначе
            // Ищем родительскую строку
            СтрокаРодитель = СоответствиеСтрок[СтрокаТаблицы.ИдентификаторРодителя];
            Если СтрокаРодитель <> Неопределено Тогда
                НоваяСтрока = СтрокаРодитель.Строки.Добавить();
            Иначе
                // Если родитель не найден, создаем как корневую
                НоваяСтрока = ДеревоЗначений.Строки.Добавить();
            КонецЕсли;
        КонецЕсли;
        
        // Копируем данные
        Для Каждого Колонка Из ДеревоЗначений.Колонки Цикл
            НоваяСтрока[Колонка.Имя] = СтрокаТаблицы[Колонка.Имя];
        КонецЦикла;
        
        // Сохраняем соответствие для поиска дочерних строк
        СоответствиеСтрок.Вставить(СтрокаТаблицы.ИдентификаторСтроки, НоваяСтрока);
        
    КонецЦикла;
    
    Возврат ДеревоЗначений;
    
КонецФункции

// УЛУЧШЕННАЯ альтернативная функция преобразования через уровни
Функция ТаблицуЗначенийВДеревоЗначенийПоУровням(ТаблицаЗначений) Экспорт
    
    ДеревоЗначений = Новый ДеревоЗначений;
    
    // Копируем колонки (исключая служебные)
    Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
        Если Колонка.Имя <> "ИдентификаторСтроки" 
            И Колонка.Имя <> "ИдентификаторРодителя" 
            И Колонка.Имя <> "УровеньБаза" Тогда
            НоваяКолонка = ДеревоЗначений.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения, Колонка.Заголовок);
            НоваяКолонка.Ширина = Колонка.Ширина;
        КонецЕсли;
    КонецЦикла;
    
    // Проверяем наличие колонки УровеньБаза
    КолонкаУровня = ТаблицаЗначений.Колонки.Найти("УровеньБаза");
    Если КолонкаУровня = Неопределено Тогда
        // Если нет колонки уровня, используем основную функцию
        Возврат ТаблицуЗначенийВДеревоЗначений(ТаблицаЗначений);
    КонецЕсли;
    
    // УБРАЛ сортировку - сохраняем исходный порядок строк!
    
    // Используем соответствие для поиска строк по идентификатору
    СоответствиеСтрок = Новый Соответствие;
    
    Для Каждого СтрокаТаблицы Из ТаблицаЗначений Цикл
        
        УровеньБаза = СтрокаТаблицы.УровеньБаза;
        
        Если УровеньБаза = 0 Тогда
            // Корневая строка
            НоваяСтрока = ДеревоЗначений.Строки.Добавить();
        Иначе
            // Ищем родительскую строку по идентификатору
            СтрокаРодитель = СоответствиеСтрок[СтрокаТаблицы.ИдентификаторРодителя];
            
            Если СтрокаРодитель <> Неопределено Тогда
                НоваяСтрока = СтрокаРодитель.Строки.Добавить();
            Иначе
                // Если родитель не найден, создаем как корневую
                НоваяСтрока = ДеревоЗначений.Строки.Добавить();
            КонецЕсли;
        КонецЕсли;
        
        // Копируем данные
        Для Каждого Колонка Из ДеревоЗначений.Колонки Цикл
            НоваяСтрока[Колонка.Имя] = СтрокаТаблицы[Колонка.Имя];
        КонецЦикла;
        
        // Сохраняем соответствие для поиска дочерних строк
        СоответствиеСтрок.Вставить(СтрокаТаблицы.ИдентификаторСтроки, НоваяСтрока);
        
    КонецЦикла;
    
    Возврат ДеревоЗначений;
    
КонецФункции

 

Также отмечу, что решение внедрено в реальную базу, оно рабочее, так что пользуйтесь. )

Вступайте в нашу телеграмм-группу Инфостарт

ДеревоЗначений в ТаблицуЗначений Таблица значений в дерево значений

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    3318    Abysswalker    7    

44

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

Порой необходимо временно отключить расширение 1С, не удаляя его, чтобы не потерять данные. Но в этом случае при каждом запуске всем будет лезть уведомление о неактивном расширении, хотя очевидно, это техническая информация, которой не стоит лишний раз пугать пользователей.

14.05.2025    6169    DeerCven    15    

57

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

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

21.05.2024    48208    dimanich70    83    

169

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    7257    6    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    60141    atdonya    31    

69

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    9034    ke.92@mail.ru    17    

68
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Sashares 33 03.06.25 12:03 Сейчас в теме
// Копируем данные из строки дерева
Для Каждого Колонка Из ТаблицаЗначений.Колонки Цикл
Если Колонка.Имя <> "ИдентификаторСтроки"
И Колонка.Имя <> "ИдентификаторРодителя"
И Колонка.Имя <> "УровеньБаза" Тогда
НоваяСтрока[Колонка.Имя] = СтрокаДерева[Колонка.Имя];
КонецЕсли;
КонецЦикла;


Зачем это делать для каждой строки дерева? Почему не получить 1 раз имена нужных колонок и заполнить сразу?

И не понял, зачем столько служебных колонок.
Можно добавить одну колонку - строка родитель, и заполнять в нее строку этой же ТЧ, которая соответствует строке родителя дерева.
5. ltfriend 04.06.25 07:00 Сейчас в теме
(1) зачем вообще получать список колонок, когда достаточно использовать ЗаполнитьЗначенияСвойств.
7. Sashares 33 04.06.25 09:54 Сейчас в теме
(5) чтобы не заполнять служебные
9. dhurricane 05.06.25 08:19 Сейчас в теме
(7) Они ведь потом ниже переопределяются, так что не страшно.
10. user700522_lerner584 08.06.25 20:01 Сейчас в теме
(7) Да к на это есть 4-ый параметр данного метода, в котором указывается перечень игнорируемых колонок.
2. andrew.ab 234 03.06.25 12:10 Сейчас в теме
в чем быстрота метода? Где замеры, где сравнение с другими методами?
BigB; skeptik2105; orakool; Sashares; +4 Ответить
3. aximo 2561 03.06.25 14:18 Сейчас в теме
прикол... в древние времена сам всякое писал - типа "читалки любого dbf в таблицу" https://infostart.ru/1c/tools/89298/

а сейчас все это готовенькое есть наверноое....
4. user613549_scratch_sv 03.06.25 22:49 Сейчас в теме
Быстрая?
Функция ТаблицаЗначенийВДеревоЗначений(ТЗ, ГруппировкаКолонки, ИтогКолонки) Экспорт
ПЗ = Новый ПостроительЗапроса;
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТЗ);//передаем ТЗ
ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
Для каждого Поле Из СтрРазделить(ГруппировкаКолонки, ", ", Ложь) Цикл
ПЗ.ИсточникДанных.Колонки[Поле].Измерение = Истина;//по этой колонке идет группировка
КонецЦикла;
Для каждого Поле Из СтрРазделить(ИтогКолонки, ", ", Ложь) Цикл
ПЗ.ИсточникДанных.Колонки[Поле].Итог = "Сумма("+Поле+")";
КонецЦикла;
ПЗ.ЗаполнитьНастройки();
ПЗ.Выполнить();
Дерево = ПЗ.Результат.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Возврат Дерево;
КонецФункции

и обратно:

Дерево.Колонки.Добавить("__врПоле", Новый ОписаниеТипов("Булево"));
ТЗ = Новый ТаблицаЗначений;
Для каждого Поле Из Дерево.Колонки Цикл ТЗ.Колонки.Добавить(Поле.Имя, Поле.ТипЗначения); КонецЦикла;
Для каждого Ст Из Дерево.Строки.НайтиСтроки(Новый Структура("__врПоле", Ложь), Истина) Цикл ЗаполнитьЗначенияСвойств(ТЗ.Добавить(), ст); КонецЦикла;
ТЗ.Колонки.Удалить("__врПоле");
CherAl; BigB; +2 Ответить
6. RustIG 1933 04.06.25 09:08 Сейчас в теме
1) есть прямой, симметричный и обратный обход дерева - у вас какой?
2) иногда нужно не обходить дерево , а найти что-то. Для поиска используется обход дерева "в глубину" или " в ширину"...

ПС. Андрей, извините за оффтопик, я давно не писал статьи - сколько сейчас начисляют за написание статей?
8. kuzyara 2219 04.06.25 10:29 Сейчас в теме
(6) https://infostart.ru/journal/news/mir-1s/izmenenie-v-pravilakh-ustarevshie-stati-v-baze-znaniy-bolshe-ne-budut-zarabatyvat-startmani_1914927/
- за публикацию статей объемом больше 10 000 знаков автор получает 10 стартмани
- если статья набирает больше 50 баллов, мы начисляем автору еще 30 стартмани
- если статья набирает больше 100 баллов, автор получает 20 стартмани
- если статья набирает более 500 баллов, за каждую последующую тысячу “лайков” от читателей, мы начислим автору 10 стартмани.
Для отправки сообщения требуется регистрация/авторизация