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

03.06.25

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

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

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

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

 

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

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

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

 

 

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

 

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

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

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

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

 

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

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

См. также

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

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

21.05.2024    34027    dimanich70    83    

154

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

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

1 стартмани

18.03.2024    5295    6    John_d    11    

57

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

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

12.02.2024    39618    atdonya    29    

62

Универсальные функции Программист Платформа 1С v8.3 Бесплатно (free)

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

30.11.2023    6936    ke.92@mail.ru    17    

66

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 1C:Бухгалтерия Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    18998    YA_418728146    8    

173

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    5133    83    progmaster    11    

4

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 1C:Бухгалтерия 1С:Бухгалтерия 3.0 1С:ERP Управление предприятием 2 1С:Управление холдингом 1С:Зарплата и Управление Персоналом 3.x 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Управление торговлей 11 1С:Розница 2 1С:Документооборот Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    20423    199    sapervodichka    113    

138
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
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) чтобы не заполнять служебные
2. andrew.ab 226 03.06.25 12:10 Сейчас в теме
в чем быстрота метода? Где замеры, где сравнение с другими методами?
orakool; Sashares; +2 Ответить
3. aximo 2342 03.06.25 14:18 Сейчас в теме
прикол... в древние времена сам всякое писал - типа "читалки любого dbf в таблицу" https://infostart.ru/1c/tools/89298/

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

и обратно:

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

ПС. Андрей, извините за оффтопик, я давно не писал статьи - сколько сейчас начисляют за написание статей?
8. kuzyara 2152 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 стартмани.
Оставьте свое сообщение