Имя: Пароль:
1C
1С v8
Алгоритм построения дерева из ТЗ
0 higelios
 
23.10.12
10:13
Собственно появилась недавно необходимость получить из внешнего источника данные и отображать их в виде дерева, для редактирования и дальнейшей загрузки в справочник. Внешний источник имеет следующую структуру данных:
КодРодителя|КодЭлемента|ПризнакГруппы|Наименование и т.д.
КодРодителя это  элемент из этой же таблицы. Вот приблизительно пример:
2|3|0|GeForce GTX560
0|1|1|Видеокарты
1|2|1|Видеокарты NVIDIA
и тд.
Помню на js делал методом вложенных массивов и хэш-таблицы, кода было строк на 10 максимум и работало весьма шустро. С деревом же так не выйдет, так как строки его переподчинить нельзя:(. Попытки написать более менее нормальный алгоритм успехом не увенчались. Выходит какой-то дикий костыль. Может у кого-то уже есть наработки в данной области?
1 Stim
 
23.10.12
10:16
рекурсия спасет отца js
2 ProProg
 
23.10.12
10:17
Курс 1С - базовые объекты.
3 х86
 
23.10.12
10:18
запрос не предлагать?
4 vmv
 
23.10.12
10:21
закинуть тз в СКД как внешний источник, создать двумая пальцами няшную групировку(дерево) и выгрузить в коллекцию дерево стандартными средствами в обработчике ПриКомпоновке....

все остальные мегаметоды буду расценивать как верх цинизма и непрофессонализма - у меня все
5 Maxus43
 
23.10.12
10:31
(4) нахрена в СКД? просто в запрос быстрее
6 higelios
 
23.10.12
10:35
Stim, Рекурсия полность здесь не спасёт здесь. Внимательно посмотрите на структуру данных.
ProProg, Это нужно посетить сначала вам, чтобы помочь мне.)
x86. Да любые решения годятся, лишь бы более менее оптимально по скорости выполнения было. Мой костылятор обрабатывает около 6000 записей вместе с запросом за 10-12 сек. Из той же таблицы через js за 4 сек.
vmv. Я бы и рад сделать группировку в запросе, но у группы могут быть такие же реквизиты как и у элемента. При группировке эти реквизиты мы теряем, и чтобы их дозаполнить нужно заново рыскать по ТЗ. Посмотрите на структуру данных. Группа храниться в той же таблице. Может я чего не понял?
7 higelios
 
23.10.12
10:49
По-моему запросом тут не получиться всё-таки. Покрутил внешний источник в конструкторе и удобоваримого результата не получил. Может есть у кого пример реализации?
8 Goggy
 
23.10.12
10:52
(7) Я тоже в школе учебник по химии в руках покрутил, но выше трояка не видал никогда :)
9 higelios
 
23.10.12
10:56
Goggy. Видимо поэтому, ничего конкретного и здесь сказать не можешь. Слабо решение предложить?
10 Kashemir
 
23.10.12
10:56
(6) Всем подходит, а тебе вдруг рекурсия не подойдет. Ты думаешь 2 поля (родитель-элемент) тз такой уникальный случай сворачивания дз ?
11 higelios
 
23.10.12
11:03
Рекурсия так или иначе в решении данного вопроса у меня используеться, но "костылятор" в красивое решение она не превращает, так как кроме неё там присутствует например поиск по дереву и ТЗ некислое кол-во раз, что алгоритм быстрым и красивым не делает.
12 Kashemir
 
23.10.12
11:06
(11) И что ? проиндексируй таблицу и будет тебе быстрый алгоритм


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


ТЗ.Индексы.Добавить("КодРодителя");

РазвернутьТЗ(ТЗ, ДЗ, 0);
13 higelios
 
23.10.12
11:15
С индексированием всё понятно и так.
А вот с рекурсией не всё так просто.
У меня например родителя с кодом 0 просто нет и поэтому предложенный алгоритм не сработает. Что на вход подать?
14 salvator
 
23.10.12
11:17
Посмотри тут:
http://help1c.com/faq8/view/1051.html
15 higelios
 
23.10.12
11:18
Спасибо. Сейчас посмотрю.
16 Kashemir
 
23.10.12
11:26
(13) Как это может быть ? У тебя нет точки отсчета структуры дерева ?
17 higelios
 
23.10.12
11:38
А откуда у меня точка отсчёта возьмётся? У меня голая ТЗ, никак не упорядоченная. Структура в 1 сообщении. Есть только признак, который говорит, что это элемент или группа и код родителя, по которому можно построить иерархическую цепочку. Верхний уровень худо белно можно определить по пустому полю КодРодителя.
18 Kashemir
 
23.10.12
11:42
(17) Странный ты человек. Пустое поле числового типа это какое число то ?
19 Classic
 
23.10.12
11:43
(13)
Ну так добавь строку с кодом 0. Будет тебе точка отсчета
20 higelios
 
23.10.12
11:43
Под пустым я подразумевал 0.
21 Kashemir
 
23.10.12
11:44
(19) Да не надо ничего добавлять.
22 Kashemir
 
23.10.12
11:44
(20) Молодец, а теперь еще раз внимательно посмотри что делает код в (12)
23 Classic
 
23.10.12
11:44
(12)
Циклы не учитываешь
24 Kashemir
 
23.10.12
11:46
(23) Какие циклы ?
25 Classic
 
23.10.12
11:46
1/2/1
2/1/1
26 Kashemir
 
23.10.12
11:47
(25) Приборы
27 Classic
 
23.10.12
11:47
Автор не гарантирует, что ТЗ из дерева получалась:)
28 higelios
 
23.10.12
11:49
ТЗ образуется из запроса к внешнему источнику данных.
+Еще нужно учитывать что если есть код родителя это не значит, что есть сам родитель, поэтому алгоритм предложенный в (14) на таких позициях "двоит".
29 Kashemir
 
23.10.12
11:50
(27) Признак группы (третье число) никакой роли вообще не играет в построении иерархии. Важен код элемента и код родителя.

2|3|0|GeForce GTX560
0|1|1|Видеокарты
1|2|1|Видеокарты NVIDIA

В примере 0 у автора иерархия получилась вполне нормальная
Видеокарты
- Видеокарты НВИДИА
- - GeForce GTX560
30 Kashemir
 
23.10.12
11:50
(28) Ясно, досвиданье. Попробовать смотрю ума не хватает
31 Classic
 
23.10.12
11:52
(29)
В примере да, но выход из рекурсии задавать все-таки надо.
Тем более в свете (14)
32 Classic
 
23.10.12
11:52
Точнее в свете (28)
33 Kashemir
 
23.10.12
11:53
(31) Блин и это после 7 лет на форуме
34 Classic
 
23.10.12
11:53
(33)
Еще раз. Что твой алгоритм выдаст на ТЗ
1/2
2/1
35 Kashemir
 
23.10.12
11:54
(34) Выдаст ровно ничего, нет точки отсчета - нет верхнего уровня. Такая иерархия висит в воздухе и смысла не имеет.
36 higelios
 
23.10.12
11:58
(35) Алгоритм в (12) не работает в моём случае. Если не веришь, могу дать исходную ТЗ.
37 Kashemir
 
23.10.12
12:03
(36) В твоем случае ДНК не работает. С алгоритмом все отлично.
38 higelios
 
23.10.12
12:05
(37) Эээ. Ты полегче. Умей конструктивно вести диалог и чужой ДНК не трогай, работай над своим. Еще раз посмотри в (36). Таблицу давать????
39 Kashemir
 
23.10.12
12:06
(38) Давай посмотрим на твой уникальный случай
40 higelios
 
23.10.12
12:07
Секунду..
41 higelios
 
23.10.12
12:26
Вот например, выдрал характерный кусок. Родителя 2644 кстати не существует. Что выдаст твой алгоритм? Что будешь подавать на вход? Алгоритм из (14) выдаст две корневые группы, хотя должна быть одна, а уже в ней другая.
ЭтоГруппа    РодительИД    Идентификатор    ВнешнееНаименование
Да    2 598    2 649    0Асtrahan2
Да    2 644    2 598    0АСtrahan
Нет    2 598    2 597    0АСTRA_01
Нет    2 649    7 875    0АСTRA_RS_LC_14
Нет    2 649    7 932    0АСTRA_АСГ_3
Нет    2 649    2 650    0АСTRA_АСГ_2
Нет    2 649    2 651    0АСTRA_АСГ_1
42 Kashemir
 
23.10.12
12:29
(41) Где точка входа ? Какая код родителя соотвествует группе максимального уровня или может нужно ли его сначала рассчитать ?
43 azernot
 
23.10.12
12:30
Код в (12) красивый. Хоть и не рабочий. Мне кажется, он прирзван продемонстрировать идею, а не использоваться тупым копипастом.
44 Kashemir
 
23.10.12
12:30
+(41) Если код несуществующего кода родителя и соотвествует верхнему уровню тогда РазвернутьТЗ(ТЗ, ДЗ, 2644);
45 azernot
 
23.10.12
12:30
СтрокиУровня = ТЗ.НайтиСтроки(Новый Структура("КодРодителя", 0<<Тут по идее должен быть УровеньРодителя>>));
46 Kashemir
 
23.10.12
12:32
(45) О точно, не заметил !
47 Kashemir
 
23.10.12
12:33
(45) Спасибо Азерноту, первый осмысленно оценил алгоритм и заметил явную очепятку. Итого:
Процедура РазвернутьТЗ(ТЗ, Коллекция, УровеньРодителя)
   СтрокиУровня = ТЗ.НайтиСтроки(Новый Структура("КодРодителя", УровеньРодителя));
   Для каждого Стр из СтрокиУровня Цикл
       НоваяВетко = Коллекция.Строки.Добавить();
       ЗаполнитьЗначенияСвойств(НоваяВетко, Стр);
       РазвернутьТЗ(ТЗ, НоваяВетко, Стр.КодЭлемента);
   КонецЦикла;
КонецПроцедуры
48 azernot
 
23.10.12
12:35
(47) Ну и как справедливо замечено в (23) ситуация, когда данные нкеорректны и родителем элемента явялется элемент, сам в свою очередь являющийся родителем первого элемента, данный код уйдёт в глухую рекурсию.
49 higelios
 
23.10.12
12:37
(48) В ТОЧКУ!
50 higelios
 
23.10.12
12:38
Кроме того точки входа просто нет. Фрагмент таблицы, который я привёл, полностью автономный.
51 Kashemir
 
23.10.12
12:39
(48) В (23) автор кода не предложил точку входа. Не будет глухой рекурсии с заданной точкой входа даже для замкнутых элементов.
52 Kashemir
 
23.10.12
12:40
(50) Так а рассчитать что мешает ?
53 azernot
 
23.10.12
12:40
(50) В этом случае, тебе сначала нужно найти все строки, родителей которых нет в исходной ТЗ и сделать их строками дерева 1-го уровня.
54 Kashemir
 
23.10.12
12:43
(51) Хотя нет - все же с кривой точкой входа глухая все же будет. Однако тут тогда будет вопрос к тому кто подал некорректного родителя на вход.
55 Kashemir
 
23.10.12
12:52
+(54) Путем несложных махинаций можно перестраховаться от замкнутых элементов с подачей кривого родителя в кольце:


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


ТЗ.Колонки.Добавить("Использован", Новый ОписаниеТипов("Булево"));
ТЗ.Индексы.Добавить("КодРодителя, Использован");

РазвернутьТЗ(ТЗ, ДЗ, 0);
56 higelios
 
23.10.12
17:13
Всем спасибо! Чуть допилил для универсальности и получилось что-то такое:


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

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


\\ВЫЗОВ
ТаблицаВДерево(ТаблицаЗначений, ДеревоЗначений, "Идентификатор","РодительИД");
57 Kashemir
 
23.10.12
17:16
(56) Ты наверно так задумывал
СтруктураОтбора=Новый Структура(КлючПоля);
58 higelios
 
23.10.12
17:23
Ага. Всё верно. Ошибочка закралась.
59 higelios
 
23.10.12
17:23
Спасибо.
60 Kashemir
 
23.10.12
17:44
(59) С точки зрения эстетики этот код лучше смотрится:


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

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


\\ВЫЗОВ
ТаблицаВДерево(ТаблицаЗначений, ДеревоЗначений, "Идентификатор","РодительИД");
61 vmv
 
23.10.12
18:03
(60) с точки зрения эстетики нужно получать плоскую таблицу запросом в СКД со всеми необходимыми для нужной группировки в дерево дополнительными полями и потом верти деревяхи как буратина мальвину до упаду, а подобные (60) алгоритмы да хороши, но я хачу канструктор и хачу чтобы в нем открылся запрос и мне плевать на эститику если этого нет
62 higelios
 
23.10.12
18:04
Согласен. Такой код более читаемый. По производительности правда не сравнивал, что будет предпочтительнее, но наверно тоже самое.
63 higelios
 
23.10.12
18:07
(61) А пример в студию? Человек в (60) готовый кусок кода привёл. Что касается СКД, то что-то я пример реализации подобной задачи на СКД так и не нашёл.
64 vmv
 
23.10.12
18:12
(63) погули мисту - этот кусок кода мну выкладывало лет 3-5 назад, когда я был наивным и групым, считая шаблонизацию запросов мегафишкой. Слава богу здравый смысл и холодный расчет в сложнгейших запросах 8.2 показал, что это тупиковая ветвь и нужно стремиться видеть любой хитровдутый запрос в контсрукторе безь проблем.

какой тебе пример, даже пример запроса  в (60) можно сделать параметризованным и убрать дурацкие вставки-шаблоны, тебя учить основам?)
65 higelios
 
23.10.12
18:24
На счет параметризации согласен. Хотя запрос маленький и всё видно. Вот когда запрос страниц на 5, а там еще 5-6 вставок, вот где жесть и "добрые" слова, когда его конструктором пытешся пнуть.
Если уже говорить о СКД, то интересует пример построения дерева на основе плоской таблицы типа(1) только средствами СКД. Вот тут например есть нечто похожее, давно еще читал, но еще не разбирался http://nashe1c.ru/materials-view.jsp?id=362
66 vmv
 
23.10.12
18:31
(65) и чо там разбираться - прочитать до диагонали и все понятно.

дальше все советы платные, ибо ты ленив не в меру)
67 Kashemir
 
23.10.12
18:46
(61) Тащить за собой воз СКД ради собственной иерархии лениво и не вижу особой целесообразности.
68 Kashemir
 
23.10.12
18:51
(65) Тебе метод по ссылке не подойдет - он заточен под ограниченную глубиной иерархии. Почитай "Разработку сложных отчетов" Хрусталевой, раздел вывода собственной иерархии.
69 higelios
 
23.10.12
18:55
(66) Что-то не убедил. Если я ленив не в меру, то ты жаден не в меру, либо всего вероятнее еще более ленив.
Пока солидарен с (67).
(68) Да, действитель, я уже это просёк. С неограниченным уровнем иерархии будет сложнее. У Хрусталёвой действительно что-то такое читал, но по-моему там тоже не совсем то было, надо посмотреть...