Пример преобразования дерева значений в таблицу значений и обратно в 1Cv8

Опубликовал Sintson в раздел Программирование - Практика программирования

Хочу поделиться с посетителями сайта своим подходом к преобразованию таблицы значений в дерево значений и обратно.
Вообще, при разработке отраслевой задачи, была необходимость почти во всех документах, выводить информацию в виде дерева и хранить ее в табличных частях документа, а также в интерактивной обработке данных в виде дерева.
Отсюда появился небольшой модуль для расширения возможности работы с деревом значений, хотя и с некоторыми оговорками.
Основной идеей является использование двух ключевых реквизитов/колонок КлючСтроки и КлючСвязи.
Однако они не всегда необходимы. Код, на мой взгляд достаточно "высушен".
Цель публикации - поделиться с новичками опытом.

////////////////////////////////////////////////////////////////////////////////
// ПРОЦЕДУРЫ И ФУНКЦИИ ДЛЯ РАБОТЫ С ДЕРЕВОМ ЗНАЧЕНИЙ

// Функция формирует значение нового ключа строки табличной части.
//
// Параметры:
// Дерево - дерево значений
//
Функция ПолучитьНовыйКлючСтрокиДерева(Дерево, СписокКлючей = Неопределено) Экспорт

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

КонецФункции
// ПолучитьНовыйКлючСтрокиДерева()

// Процедура обновляет ключи связи в дереве значений
//
Процедура ОбновитьКлючиСвязиВДеревеЗначений(Дерево) Экспорт

    Для Каждого
СтрокаДерева Из Дерево.Строки Цикл
        Попытка
           
СтрокаДерева.КлючСвязи = СтрокаДерева.Родитель.КлючСтроки;
        Исключение
           
СтрокаДерева.КлючСвязи = 0;
        КонецПопытки;
       
ОбновитьКлючиСвязиВДеревеЗначений(СтрокаДерева);
    КонецЦикла;

КонецПроцедуры
// ОбновитьКлючиСвязиВДеревеЗначений()

// Процедура обновляет ключи связи в дереве значений
//
Процедура ОбновитьКлючиСтрокВДеревеЗначений(Дерево, КлючСтроки = 1) Экспорт

    Для Каждого
СтрокаДерева Из Дерево.Строки Цикл
       
СтрокаДерева.КлючСтроки = КлючСтроки;
       
КлючСтроки = КлючСтроки + 1;
       
ОбновитьКлючиСтрокВДеревеЗначений(СтрокаДерева, КлючСтроки);
    КонецЦикла;

КонецПроцедуры
// ОбновитьКлючиСвязиВДеревеЗначений()

// Процедура выгружает данные из дерева значений в таблицу значений
// данные выгружаются только в таблицу со сходным набором реквизитов
//
Функция ВыгрузитьДеревоЗначенийВТаблицуЗначений(Дерево, Таблица = Неопределено) Экспорт

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

КонецФункции
//ВыгрузитьДеревоЗначенийВТаблицуЗначений()

// Процедура выгружает данные из таблицы значений в дерево значений
// данные выгружаются только в таблицу со сходным набором реквизитов
//
// Параметры:
//
// КлючСтроки - имя колонки ТаблицыЗначений - уникальный идентификатор
// КлючСвязи - имя колонки ТаблицыЗначений - указатель привязки к строке Дерева,
// своего рода указатель на "Родителя"
//
Функция ВыгрузитьТаблицуЗначенийВДеревоЗначений(Таблица, КлючСтроки = "КлючСтроки", КлючСвязи = "КлючСвязи") Экспорт

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

КонецФункции
//ВыгрузитьТаблицуЗначенийВДеревоЗначений()

// Процедура устанавливает значение во всем дереве значений
//
Процедура УстановитьЗначениеКолонкиДерева(Дерево, Колонка, Значение) Экспорт

    Для каждого
СтрокаДерева Из Дерево.Строки Цикл
       
СтрокаДерева[Колонка] = Значение;
       
УстановитьЗначениеКолонкиДерева(СтрокаДерева, Колонка, Значение);
    КонецЦикла;

КонецПроцедуры
//УстановитьЗначениеКолонкиДерева()

// Процедура копирует подчиненные строки дерева значений
//
Процедура СкопироватьПодчиненныеСтроки(СтрокаПриемник, СтрокаИсточник)

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

КонецПроцедуры
// СкопироватьПодчиненныеСтроки()

// Процедура переносит выделенные строки дерева значений в указанную ветку
//
Процедура ПеренестиСтрокиДереваЗначений(СтрокаПриемник, ВыделенныеСтроки) Экспорт

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

КонецПроцедуры
//ПеренестиСтрокиДереваЗначений()


Файлы

Наименование Файл Дата Размер Кол. Скачив.
Конфигурация с примером использования в документах.
.dt 15,76Kb
13.08.10
418
.dt 13.08.10 15,76Kb 418 Скачать

См. также

Лучшие комментарии

16. Sergey K 11.08.2010 07:59
Пример выгрузка Таблицы в Дерево..
1. в твой код необходимо вставить:
Таблица.Сортировать("КлючСтроки");
т.к. если в таблице строки не будут упорядочены по КлючуСтроки, то у тебя дерево построится неправильно

2. СтрокаГруппировки = Дерево.Строки.Найти(СтрокаТаблицы[КлючСвязи], КлючСтроки,Истина);

достаточно медленая, тем более ты ищешь по всем уровням дерева..

мой вариант:
числоЭлементов = Таблица.Количество();
Если числоЭлементов = 0 Тогда
Возврат;
КонецЕсли;
МассивСтрок = Новый Массив(Таблица[числоЭлементов-1]["КлючСтроки"]);

Для каждого Строка Из Таблица Цикл
Если Строка.КлючСвязи = 0 Тогда
МассивСтрок.Вставить(КлючСтроки,Дерево.Строки.Добавить());
Иначе
МассивСтрок.Вставить(КлючСтроки,МассивСтрок[Строка.КлючСвязи.Строки.Добавить());
КонецЕсли;
ЗаполнитьЗначенияСвойств(МассивСтрок["КлючСтроки"], Строка);
Ответили: (17) (20) (34) (36) (37)
# Ответить
32. orefkov 13.06.2012 15:22
(11)
Для универсального алгоритма достаточно ключи назвать id и parentid :)
# Ответить
20. Sintson 13.08.2010 00:11
(18) Выложил простой пример использования процедур для табличной части документа.
(16) Проблем с построением ни разу за все время не было, во всяком случае если применять так как в конфигурации с примером ;) .
+ 1 [ Asdam; ]
# Ответить

Комментарии

1. Поручик (файл скачал) 09.08.2010 14:14
Программный код не хотите разукрасить?
http://infostart.ru/public/19856/
+ 1 [ artbear; ]
# Ответить
2. Sintson 09.08.2010 14:23
(1) Спасибо за наводку, разукрасил :)
# Ответить
3. Арчибальд 09.08.2010 16:29
Меня бы больше порадовал алгоритм представления дерева в виде таблицы значений в "чистом виде", без привязки к восьмерке. Ну, хоть так...
Ответили: (5)
# Ответить
4. Yashazz 09.08.2010 17:28
А ещё такие фишки можно проделывать, используя построитель или СКД. Иной раз гибче выходит, хотя с оформлением источника данных, конечно, морока.
Ответили: (5)
+ 1 [ hulio; ]
# Ответить
5. Sintson 09.08.2010 18:02
(3) Если Вы поясните, что имеется в виду под "чистым видом", можно подумать, хотя рекурсия - она и в Африке рекурсия :) .
(4) Абсолютно согласен, можно и ПостроительОтчетов использовать.
Ответили: (6)
# Ответить
6. Арчибальд 09.08.2010 18:19
(5) Дерево и таблица существуют независимо от восьмерочной платформы. Соответственно и алгоритм представления дерева таблицей не имеет отношения к восьмерочной платформе. Т.е. задача, решенная в публикации, сильно заужена, является очень частным случаем.
Это не в укор, а мечтательно.
Ответили: (7) (8)
# Ответить
7. venger 09.08.2010 21:02
(6)
Это не в укор, а мечтательно


Присоединяюсь, даешь универсальный алгоритм;-)
Ответили: (8)
# Ответить
8. Sintson 09.08.2010 23:09
(6)(7) Вызов принимается, как прикажете оформить?
Ответили: (10)
# Ответить
9. Sergey K 10.08.2010 06:35
(0) Задача тривиальная.. добавить КлючСтроки и КлючСвязи (я использую даже точно такие же наименования - совпадение?)
Но данная реализация - достаточно медленная..
можно обойтись без использования метода "Найти", "СортироватьПоЗначению" - очень замедляет весь процесс
но, всеравно поставлю + :)
Ответили: (12) (13) (15) (19)
# Ответить
10. Арчибальд 10.08.2010 07:25
(8) Лучше всего (наглядней и прозрачней) - в виде комикса.
Ответили: (11) (13) (14)
+ 1 [ adhocprog; ]
# Ответить
11. venger 10.08.2010 10:52
(10) Это как? В виде алгоритма - знаю, в виде комикса - это как?;-)
Ответили: (32)
# Ответить
12. Sintson 10.08.2010 11:09
(9) Реверанс
# Ответить
13. Sintson 10.08.2010 11:12
(9) Реверанс
(10) Я пас, алгоритмы в виде комикса для меня слишком 8-)
Ответили: (14)
# Ответить
14. venger 10.08.2010 11:32
(10) (13) Вот, спугнули;-) Хорошо хоть не в виде мульта;-)
# Ответить
15. Sintson 11.08.2010 03:26
(9) может поделитесь реализацией? Хотелось бы взглянуть так сказать в метадле...
# Ответить
16. Sergey K 11.08.2010 07:59
Пример выгрузка Таблицы в Дерево..
1. в твой код необходимо вставить:
Таблица.Сортировать("КлючСтроки");
т.к. если в таблице строки не будут упорядочены по КлючуСтроки, то у тебя дерево построится неправильно

2. СтрокаГруппировки = Дерево.Строки.Найти(СтрокаТаблицы[КлючСвязи], КлючСтроки,Истина);

достаточно медленая, тем более ты ищешь по всем уровням дерева..

мой вариант:
числоЭлементов = Таблица.Количество();
Если числоЭлементов = 0 Тогда
Возврат;
КонецЕсли;
МассивСтрок = Новый Массив(Таблица[числоЭлементов-1]["КлючСтроки"]);

Для каждого Строка Из Таблица Цикл
Если Строка.КлючСвязи = 0 Тогда
МассивСтрок.Вставить(КлючСтроки,Дерево.Строки.Добавить());
Иначе
МассивСтрок.Вставить(КлючСтроки,МассивСтрок[Строка.КлючСвязи.Строки.Добавить());
КонецЕсли;
ЗаполнитьЗначенияСвойств(МассивСтрок["КлючСтроки"], Строка);
Ответили: (17) (20) (34) (36) (37)
# Ответить
17. Sintson 11.08.2010 12:25
(16) С первым согласен, однако если таблицу мы получаем из дерева - она так и так будет отсортирована. В моем случае в качестве таблицы выступала табличная часть документа для хранения данных.
Со вторым полностью согласен, достойный вариант построения.
# Ответить
18. Asdam (файл скачал) 12.08.2010 09:13
Sintson, выложите, плиз, файл конфигурации с примером документа для чайников.
Ответили: (20)
# Ответить
19. Asdam (файл скачал) 12.08.2010 09:15
(9) Sergey K, выложите, плиз, Ваш пример реализации преобразования дерева значений в таблицу значений и обратно.
# Ответить
20. Sintson 13.08.2010 00:11
(18) Выложил простой пример использования процедур для табличной части документа.
(16) Проблем с построением ни разу за все время не было, во всяком случае если применять так как в конфигурации с примером ;) .
+ 1 [ Asdam; ]
# Ответить
21. kisyalort (файл скачал) 31.08.2010 17:16
Спасибо большое! :)
Ответили: (22)
# Ответить
22. Sintson 31.08.2010 18:52
(21) Пожалуйста, заходите еще ;)
# Ответить
23. villy (файл скачал) 07.09.2010 17:06
а как заставить заработать сабж в 1С: Предприятие 8.2 (управляемое приложение)?
Ответили: (25)
# Ответить
24. 1malder1 (файл скачал) 01.10.2010 12:10
Может у кого есть пример вывода дерева в печатную форму(в виде дерева), нужно сделать для обычной и управляемой формы
Ответили: (26)
# Ответить
25. Sintson 07.10.2010 23:43
(23) В 1С: Предприятие 8.2, как вариант рекомендую создать реквизит с типом хранилище значения и восстанавливать перед открытием и сохранять при закрытии формы.

Чтобы заставить работать мои процедуры - их нужно просто немного переписать, учитывая специфику управляемого приложения.
# Ответить
26. Sintson 07.10.2010 23:49
(24) Вывод в печатную форму из дерева значений осуществляется с использованием рекурсии.
Можно также запихнуть его в СКД, используя как внешние данные.
# Ответить
27. Kotta (файл скачал) 22.09.2011 15:39
Спасибо, очень пригодилось.
# Ответить
28. adhocprog 11.01.2012 16:05
Большое спасибо!
Картинка тоже порадовала! :)
Ответили: (29)
+ 1 [ Sintson; ]
# Ответить
29. Sintson 11.01.2012 22:50
(28) Ценю чувство юмора, спасибо!
Ответили: (46)
# Ответить
30. takeshi3 27.02.2012 12:53
Спасибо!Поставил плюсик!
# Ответить
31. EfremoVich (файл скачал) 13.04.2012 13:23
Вот бы ещё получилось бы скачать конфу для изучения.. ;(
# Ответить
32. orefkov 13.06.2012 15:22
(11)
Для универсального алгоритма достаточно ключи назвать id и parentid :)
# Ответить
33. tindir 03.07.2012 13:04
Отличное подспорье для новичков. Сидел и печально придумывал хитрые рекурсии на листочке, пока не наткнулся на ваш пример. Кстати, по поводу оптимизации на сколько пример с массивами будет работать быстрее. вопрос не пустой, мне в дерево надо будет перегонять таблицу с количеством строк от 10 до 6000 ( ух и огромная организационная структура у этой организации :-) )
# Ответить
34. tindir 03.07.2012 13:07
(16) Sergey K, пока не разорался как ваш кусок кода работает. и думаю стоит ли (в целях самообразованя -да, а вот в целях разработки..). на сколько он будет эффективнее, чем поиск с "найти" и "сортировать" ? (дерево будет строиться минимум из 10-15 элементов, максимум 6000.
Ответили: (35) (36)
+ 1 [ shegarka; ]
# Ответить
35. Sintson 03.07.2012 15:27
(34) Спасибо за комментарий, однако, задачка у Вас!
А может, пока на берегу, пересмотрите подходы к реализации?
Дерево это конечно интересно, но 6000 элементов!!!
Не лучше ли попробовать использовать динамические списки?
А то, опираясь на личный опыт, с такими объемами легко словить "ошибку памяти по адресу"
или "неизвестную ошибку компоненты C++"
, а то и банальное "недостаточно памяти"...
Ответили: (37)
# Ответить
36. Sergey K 03.07.2012 15:45
(34) Чем больше элементов в дереве, тем в разы быстрее будет работать вариант в (16)
С вариантом в (16) выводил деревья в сотни тыс. строк.
# Ответить
37. tindir 06.07.2012 07:24
(35) Sintson, да. после прочтения вашего комментария начал смотреть в сторону дим.спискоков, но пришел начальник, настучал по головушке, без того опухшей, за неправильное соединение таблиц и теперь работаем с таблице в максимум 100 строк. Производительность посмотрел. Да чем больше объем, тем более явно выражается производительность (16).

Кстати, нет ли возможности напрямую передать через ком-соединение дерево? Т.к. первоначально стоит задача сделать дерево с возможность множественного выбора как в примере. Но есть одна беда - Ком-соединение.
Сейчас я делаю как : получаю Ком-таблицу вида (Ключ|КлючРодитель|Наименование), преобразую своей процедурой в Тз и прогоняю через вашу функцию. Получив дерево, отправляю его уже "разделанную" под мои нужды обработку из примера выше и получаю результат. Вся эта абра-кадабра очень медленная и порой дерево строится не верно.
По этому и вопрос - есть ли варианты напрямую передавать дерево (может не в явном, но с меньшим количество шагов) ?
Ответили: (38)
# Ответить
38. Sergey K 06.07.2012 08:21
(37)ЗначениеВСтрокуВнутр()
Ответили: (39)
# Ответить
39. tindir 06.07.2012 11:14
(38) Sergey K, XML!!! ДА! как раньше сам до этого не додумался! Спасибо!
# Ответить
40. stalker18 04.04.2013 17:14
Хочу предложить свою реализацию перевода таблицы в дерево значений:

Функция тз_в_дз(тз_Ссылка, дз_Ссылка, КлючСвязи=0)
м_Строки=тз_Ссылка.НайтиСтроки(Новый Структура("КлючСвязи", КлючСвязи));
Для Каждого ъ_Строка Из м_Строки Цикл
ъ_Строка_ДЗ=дз_Ссылка.Строки.Добавить();
ЗаполнитьЗначенияСвойств(ъ_Строка_ДЗ, ъ_Строка);
тз_в_дз(тз_Ссылка, ъ_Строка_ДЗ, ъ_Строка_ДЗ.КлючСтроки);
КонецЦикла;
КонецФункции


Предварительно в таблице необходимо добавить индекс для ускорения поиска строк:
тз_Ссылка.Индексы.Добавить("КлючСвязи");

Вызов:
тз_в_дз(тз_Источник, дз_Приемник);
+ 1 [ adhocprog; ]
# Ответить
41. UJF (файл скачал) 09.10.2013 15:57
Конфа пустая
# Ответить
42. UJF (файл скачал) 09.10.2013 15:59
конфа нормальная
Ответили: (43)
# Ответить
43. Sintson 30.10.2013 23:23
(42) в контексте публикации, предлагаю ознакомиться с рабочей конфигурацией, где этот модуль является центральным для визуализации http://infostart.ru/public/205664/
# Ответить
44. yandextesting 11.09.2014 16:24
похожее решение - http://infostart.ru/public/281131/
# Ответить
45. KroVladS (файл скачал) 08.10.2014 10:45
(0) Ещё бы найти пример как в ТЧ загнать справочник номенклатуры с иерархией из запроса.
# Ответить
46. ChelyapinDemlink 16.05.2016 20:19
(29) Sintson, а подскажите, что передавать в качестве КлючСвязи?
Функция ВыгрузитьТаблицуЗначенийВДеревоЗначений(Таблица, КлючСтроки = "КлючСтроки", КлючСвязи = "КлючСвязи")
Ответили: (47)
# Ответить
47. Sintson 24.05.2016 00:11
(46) Это название реквизита, близкий по смыслу к аналогу "родитель" в иерархическом справочнике.
# Ответить
Внимание! За постинг в данном форуме $m не начисляются.
Внимание! Для написания сообщения необходимо авторизоваться
Текст сообщения*
Прикрепить файл