IE2017

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

Программирование - Практика программирования

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

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

// Функция формирует значение нового ключа строки табличной части.
//
// Параметры:
// Дерево - дерево значений
//

Функция ПолучитьНовыйКлючСтрокиДерева(Дерево, СписокКлючей = Неопределено) Экспорт

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

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

// Процедура обновляет ключи связи в дереве значений
//

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

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

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

// Процедура обновляет ключи связи в дереве значений
//

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

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

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

// Процедура выгружает данные из дерева значений в таблицу значений
// данные выгружаются только в таблицу со сходным набором реквизитов
//

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

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

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

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

Функция ВыгрузитьТаблицуЗначенийВДеревоЗначений(Таблица, КлючСтроки = "КлючСтроки", КлючСвязи = "КлючСвязи") Экспорт

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

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

// Процедура устанавливает значение во всем дереве значений
//

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

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

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

// Процедура копирует подчиненные строки дерева значений
//

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

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

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

// Процедура переносит выделенные строки дерева значений в указанную ветку
//

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

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

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

В 

Скачать файлы

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

См. также

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


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

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

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

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

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


Redhatych; adhocprog; itc_Geo; ikekoval; artbear; +5 Ответить 5
17. Александр Синцов (Sintson) 337 11.08.10 12:25 Сейчас в теме
(16) С первым согласен, однако если таблицу мы получаем из дерева - она так и так будет отсортирована. В моем случае в качестве таблицы выступала табличная часть документа для хранения данных.
Со вторым полностью согласен, достойный вариант построения.
18. Asdam (Asdam) 111 12.08.10 09:13 Сейчас в теме
Sintson, выложите, плиз, файл конфигурации с примером документа для чайников.
19. Asdam (Asdam) 111 12.08.10 09:15 Сейчас в теме
(9) Sergey K, выложите, плиз, Ваш пример реализации преобразования дерева значений в таблицу значений и обратно.
20. Александр Синцов (Sintson) 337 13.08.10 00:11 Сейчас в теме
(18) Выложил простой пример использования процедур для табличной части документа.
(16) Проблем с построением ни разу за все время не было, во всяком случае если применять так как в конфигурации с примером ;) .
21. Елена Ортякова (kisyalort) 31.08.10 17:16 Сейчас в теме
22. Александр Синцов (Sintson) 337 31.08.10 18:52 Сейчас в теме
(21) Пожалуйста, заходите еще ;)
23. villy (vitaliy.ermolenko) 07.09.10 17:06 Сейчас в теме
а как заставить заработать сабж в 1С: Предприятие 8.2 (управляемое приложение)?
24. Владимир Кухар (1malder1) 7 01.10.10 12:10 Сейчас в теме
Может у кого есть пример вывода дерева в печатную форму(в виде дерева), нужно сделать для обычной и управляемой формы
25. Александр Синцов (Sintson) 337 07.10.10 23:43 Сейчас в теме
(23) В 1С: Предприятие 8.2, как вариант рекомендую создать реквизит с типом хранилище значения и восстанавливать перед открытием и сохранять при закрытии формы.

Чтобы заставить работать мои процедуры - их нужно просто немного переписать, учитывая специфику управляемого приложения.
26. Александр Синцов (Sintson) 337 07.10.10 23:49 Сейчас в теме
(24) Вывод в печатную форму из дерева значений осуществляется с использованием рекурсии.
Можно также запихнуть его в СКД, используя как внешние данные.
27. Екатерина Михно (Kotta) 13 22.09.11 15:39 Сейчас в теме
Спасибо, очень пригодилось.
28. Владимир Гусев (adhocprog) 1095 11.01.12 16:05 Сейчас в теме
Большое спасибо!
Картинка тоже порадовала! :)
29. Александр Синцов (Sintson) 337 11.01.12 22:50 Сейчас в теме
(28) Ценю чувство юмора, спасибо!
30. Антон Цыкин (takeshi3) 27.02.12 12:53 Сейчас в теме
Спасибо!Поставил плюсик!
31. Александр Ефремов (EfremoVich) 13.04.12 13:23 Сейчас в теме
Вот бы ещё получилось бы скачать конфу для изучения.. ;(
32. Александр Орефков (orefkov) 1467 13.06.12 15:22 Сейчас в теме
(11)
Для универсального алгоритма достаточно ключи назвать id и parentid :)
adhocprog; ikekoval; venger; +3 Ответить
33. Вадим . (tindir) 03.07.12 13:04 Сейчас в теме
Отличное подспорье для новичков. Сидел и печально придумывал хитрые рекурсии на листочке, пока не наткнулся на ваш пример. Кстати, по поводу оптимизации на сколько пример с массивами будет работать быстрее. вопрос не пустой, мне в дерево надо будет перегонять таблицу с количеством строк от 10 до 6000 ( ух и огромная организационная структура у этой организации :-) )
34. Вадим . (tindir) 03.07.12 13:07 Сейчас в теме
(16) Sergey K, пока не разорался как ваш кусок кода работает. и думаю стоит ли (в целях самообразованя -да, а вот в целях разработки..). на сколько он будет эффективнее, чем поиск с "найти" и "сортировать" ? (дерево будет строиться минимум из 10-15 элементов, максимум 6000.
35. Александр Синцов (Sintson) 337 03.07.12 15:27 Сейчас в теме
(34) Спасибо за комментарий, однако, задачка у Вас!
А может, пока на берегу, пересмотрите подходы к реализации?
Дерево это конечно интересно, но 6000 элементов!!!
Не лучше ли попробовать использовать динамические списки?
А то, опираясь на личный опыт, с такими объемами легко словить "ошибку памяти по адресу"
или "неизвестную ошибку компоненты C++"
, а то и банальное "недостаточно памяти"...
36. Sergey (Sergey K) 65 03.07.12 15:45 Сейчас в теме
(34) Чем больше элементов в дереве, тем в разы быстрее будет работать вариант в (16)
С вариантом в (16) выводил деревья в сотни тыс. строк.
37. Вадим . (tindir) 06.07.12 07:24 Сейчас в теме
(35) Sintson, да. после прочтения вашего комментария начал смотреть в сторону дим.спискоков, но пришел начальник, настучал по головушке, без того опухшей, за неправильное соединение таблиц и теперь работаем с таблице в максимум 100 строк. Производительность посмотрел. Да чем больше объем, тем более явно выражается производительность (16).

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

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


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

Вызов:
тз_в_дз(тз_Источник, дз_Приемник);
remark; adhocprog; +2 Ответить
41. jack eee (UJF) 09.10.13 15:57 Сейчас в теме
42. jack eee (UJF) 09.10.13 15:59 Сейчас в теме
43. Александр Синцов (Sintson) 337 30.10.13 23:23 Сейчас в теме
(42) в контексте публикации, предлагаю ознакомиться с рабочей конфигурацией, где этот модуль является центральным для визуализации http://infostart.ru/public/205664/
44. Максим Полянский (yandextesting) 140 11.09.14 16:24 Сейчас в теме
45. Vladimir K (KroVladS) 08.10.14 10:45 Сейчас в теме
(0) Ещё бы найти пример как в ТЧ загнать справочник номенклатуры с иерархией из запроса.
46. Евгений Челяпин (ChelyapinDemlink) 16.05.16 20:19 Сейчас в теме
(29) Sintson, а подскажите, что передавать в качестве КлючСвязи?
Функция ВыгрузитьТаблицуЗначенийВДеревоЗначений(Таблица, КлючСтроки = "КлючСтроки", КлючСвязи = "КлючСвязи")
47. Александр Синцов (Sintson) 337 24.05.16 00:11 Сейчас в теме
(46) Это название реквизита, близкий по смыслу к аналогу "родитель" в иерархическом справочнике.
48. sv c (SvetaS2014) 17.02.17 19:01 Сейчас в теме
не открывается -"Невосстановимая ошибка
Ошибка при выполнении запроса GET к ресурсу /e1cib/userSettings:
по причине:
Ошибка SDBL:
В схеме базы данных нет таблицы с именем SystemSettings (pos=20)"
49. Александр Синцов (Sintson) 337 19.02.17 16:24 Сейчас в теме
(48) Возможно требуется конвертация, конфигурация по-моему 8.1.
50. sv c (SvetaS2014) 19.02.17 21:25 Сейчас в теме
конфигуратор пишет "Невосстановимая ошибка" и вылетает.... Как её открыть??
51. sv c (SvetaS2014) 19.02.17 21:27 Сейчас в теме
чем её конвертировать? конфигуратор вылетает...
52. Alister (Alister) 9 19.02.17 21:29 Сейчас в теме
Попробуйте не 8.3.9, а 8.3.8 или сначала 8.2, а потом уже 8.3
53. Александр Синцов (Sintson) 337 20.02.17 09:54 Сейчас в теме
(51) Коллега Alister прав. Конфигурация рабочая, у меня в 8.3.8 не конвертится напрямую из 8.1. Найдите платформу 8.2. или 8.1. и я думаю все получится.
54. Alister (Alister) 9 20.02.17 14:15 Сейчас в теме
55. S KoR (Tanis) 15.03.17 18:30 Сейчас в теме
Добрый вечер!
Подскажите, пожалуйста, как и в какой последовательности обращаться к функциям/процедурам, для преобразования дерева значений в таблицу значений?
Сейчас поставил первое обращение к ПолучитьНовыйКлючСтрокиДерева(Дерево, СписокКлючей = Неопределено) Экспорт , а в ответ ошибка. У моего дерева нет ключа строки....
56. Александр Синцов (Sintson) 337 15.03.17 22:19 Сейчас в теме
(55) Добрый вечер!
Я бы посоветовал Вам скачать демо конфигурацию, там пример использования.
А вообще этот реквизит необходим, добавьте в дерево эту колонку.
57. S KoR (Tanis) 16.03.17 13:04 Сейчас в теме
Добрый день!
Спасибо! За совет. Правда, не написал сразу, и еще не пробовал добавить поле. Но у меня дерево виртуальное, там этот Ключ строки поставить в цикле - "н + 1" ? Или как-то по другому алгоритму? Что родитель - 1, а подчиненные 2....н, дерево двухуровневое.
Спасибо!
58. Александр Синцов (Sintson) 337 16.03.17 18:17 Сейчас в теме
1. Добавьте в своем дереве колонки "КлючСтроки" и "КлючСвязи"
2. Запустите последовательно процедуры:
ОбновитьКлючиСтрокВДеревеЗначений(ВашеДерево)
ОбновитьКлючиСвязиВДеревеЗначений(ВашеДерево)
3. ВашеДерево готово для дальнейшей работы по теме
Оставьте свое сообщение