Имя: Пароль:
IT
 
Преобразовать Таблицу Значений в Дерево
0 jk3
 
10.12.09
17:09
Например, есть исходная Таблица Значений (именно так, надо немного абстрагироваться от 1С :))

n=9 строк
k=4 колонок, по значениям в которых будет строится дерево
r=2 числовых колонок, значения которых будут суммироваться


  1) 2) 3) k) 1) r)

1) a  a  a  a  1  1
2) b  a  a  a  1  2
3) b  a  a  b  1  3
4) b  a  b  a  1  4
5) c  a  a  a  1  5
6) c  b  a  a  1  6
7) c  b  a  b  1  7
8) c  b  a  c  1  8
n) c  b  b  a  1  9


На выходе должно получиться Дерево, состоящее r+2 колонок.
В 1-ой колонке вложенная ТЗ, если она есть
Во 2-ой колонке значение в узле дерева (одно из k колонок)
В остальных r колонках числовые данные

В данном случае получится такое Дерево (значения числовых колонок взяты в скобки):

ТЗ a (1 1)
   ТЗ a (1 1)
       ТЗ a (1 1)
           -- a (1 1)
ТЗ b (3 9)
   ТЗ a (3 9)
       ТЗ a (2 5)
           -- a (1 2)
           -- b (1 3)
       ТЗ b (1 4)
           -- a (1 4)
ТЗ c (5 35)
   ТЗ a (1 5)
       ТЗ a (1 5)
           -- a (1 5)
   ТЗ b (4 30)
       ТЗ a (3 21)
           -- a (1 6)
           -- b (1 7)
           -- c (1 8)
       ТЗ b (1 9)
           -- a (1 9)

Нужен рекурсивный алгоритм, который построит такое дерево при заданных n, k, r.
1 Ненавижу 1С
 
гуру
10.12.09
17:15
Отбираешь строки по первому столбцу, по которому строится дерево, выгружаешь это в ТЗ, добавляешь в дерево узел, для этой ТЗ делаешь рекурсивно
как то так
2 Turpentine
 
10.12.09
17:17
А на хрена рекурсия. Можно конечно и рекурсией и не сложно.
Но ГОРАЗДО проще запросом к таблице с итогами и выгрузкой результата "с группировкой"
3 Жан Пердежон
 
10.12.09
17:22
дерево из колонок? интересно-интересно...
кто вкурил - отсыпьте
4 Жан Пердежон
 
10.12.09
17:25
поднатужился - получилось представить только колонки из дерева
5 Serginio1
 
10.12.09
17:30
6 jk3
 
10.12.09
17:39
(5) ну и, где там дерево, которое я описал в (0) ?
7 Serginio1
 
10.12.09
17:45
Применяешь глСгруппироватьПоПолюСТЗБыстр(Тз,Поле) к каждой группировке и заменяешь ТзПоГруппе на глСгруппироватьПоПолюСТЗБыстр(ТзПоГруппе,Поле) и так пока полей хватит
8 Serginio1
 
10.12.09
17:52
Процедурка СгруппироватьТз(Тз,СписокПолей,Индекс,КоличествоПолей)
Тз.ВыбратьСтроки();
Пока Тз.ПолучитьСтроку()=1 Цикл
Тз.ТзПоГруппе=глСгруппироватьПоПолюСТЗБыстр(Тз.ТзПоГруппе,СписокПолей.ПолучитьЗначение(Индекс);
Если Индекс<=КоличествоПолей Тогда
СгруппироватьТз(Тз.ТзПоГруппе,СписокПолей,Индекс+1,КоличествоПолей)
КонецЕсли;
КонецЦикла;
КонецПроцедурки

Сперва Нужно сделать первичную группировку, где тз это плоская таблица
Тз=СгруппироватьТз(Тз,СписокПолей,Индекс,КоличествоПолей)
9 Serginio1
 
10.12.09
18:02
Тьфу Сначала
Получить Первичную Группировочную Таблицу
Тз=глСгруппироватьПоПолюСТЗБыстр(,Поле);
А затем ее дербанить на оствшиеся поля
СгруппироватьТз(Тз,СписокПолей,1,КоличествоПолей)
10 Ёпрст
 
10.12.09
18:02
(0) Индексированная ТЗ.. метод Группировать()
11 Mitriy
 
10.12.09
18:07
(10) это что? О_О
12 Ёпрст
 
10.12.09
18:12
13 Ёпрст
 
10.12.09
18:13
+12 Для отчета, можно еще Класс.ИтогиПоГруппировкам задействовать..
14 jk3
 
10.12.09
19:59
(10) индексированная таблица -- это конечно хорошо, но нужен сам алгоритм, реализованный на 7-ке и который можно потом будет с минимальным допиливанием перенести на 8-ку
оптимальность/скорость работы не играет особой роли
15 Барбариска
 
10.12.09
20:07
Посмотри такое. Внимательно не вчитывалась в условие, но вроде это то... ))

//************************************************************
Функция ИтоговаяТЗ(ТЗНач, СпКолонокГрупп, СпКолонокСумма, НачУровень = 0, СтруктураВнутр = "") Экспорт
   Перем ТЗНачКопия, ТЗИтог, ТекТЗ, ВремТЗ;
   ТЗНач.Выгрузить(ТЗНачКопия);
   Если НачУровень = 0 Тогда
       ТЗНачКопия.Сортировать(СпКолонокГрупп);
       СтруктураВнутр = ЗначениеВСтрокуВнутр(СоздатьОбъект("ТаблицаЗначений"));
   КонецЕсли;
   ТЗНачКопия.Выгрузить(ТЗИтог);
   ТЗНачКопия.Выгрузить(ВремТЗ);
   Поз = Найти(СпКолонокГрупп, ",");
   Если Поз > 0 Тогда
       ТекИзм = СокрЛП(Лев(СпКолонокГрупп, Поз - 1));
       ОстИзм = СокрЛП(Сред(СпКолонокГрупп, Поз + 1));
   Иначе
       ТекИзм = СокрЛП(СпКолонокГрупп);
       ОстИзм = "";
   КонецЕсли;
   ТЗИтог.Свернуть(ТекИзм, СпКолонокСумма);
   Если ОстИзм = "" Тогда
       // по строкам - заполнить таблицы значений
       ТЗИтог.НоваяКолонка("ТаблицаЗначений");
       ТЗИтог.ВыбратьСтроки();
       Пока ТЗИтог.ПолучитьСтроку() = 1 Цикл
           ТЗИтог.ТаблицаЗначений = ЗначениеИзСтрокиВнутр(СтруктураВнутр);
       КонецЦикла;
       Возврат ТЗИтог;
   КонецЕсли;
   Если ТЗИтог.КоличествоСтрок() = 0 Тогда
       Возврат ТЗИтог;
   КонецЕсли;
   ТЗИтог.НоваяКолонка("ТаблицаЗначений");
   ТЗИтог.НоваяКолонка("СлужебнаяНач","Число");
   ТЗИтог.НоваяКолонка("СлужебнаяКон" ,"Число");
   ТЗИтог.ВыбратьСтроки();
   Пока ТЗИтог.ПолучитьСтроку() = 1 Цикл
       НомСтр = 0;
       ВремТЗ.НайтиЗначение(ТЗИтог.ПолучитьЗначение(ТЗИтог.НомерСтроки, ТекИзм), НомСтр, ТекИзм);
       ТЗИтог.СлужебнаяНач = НомСтр;
       Если ТЗИтог.НомерСтроки <> 1 Тогда
           ТЗИтог.УстановитьЗначение(ТЗИтог.НомерСтроки - 1, "СлужебнаяКон", НомСтр - 1);
       КонецЕсли;
   КонецЦикла;
   ТЗИтог.УстановитьЗначение(ТЗИтог.КоличествоСтрок(), "СлужебнаяКон", ВремТЗ.КоличествоСтрок());
   ТЗИтог.ВыбратьСтроки();
   Пока ТЗИтог.ПолучитьСтроку() = 1 Цикл
       ТЗИтог.Выгрузить(ТекТЗ, 1, 1, 1);
       ВремТЗ.Выгрузить(ТекТЗ, ТЗИтог.СлужебнаяНач, ТЗИтог.СлужебнаяКон, ОстИзм + "," + СпКолонокСумма);
       ТекТЗ.Свернуть(ОстИзм, СпКолонокСумма);
       ТЗИтог.ТаблицаЗначений = ИтоговаяТЗ(ТекТЗ, ОстИзм, СпКолонокСумма, 1, СтруктураВнутр);
   КонецЦикла;
   ТЗИтог.УдалитьКолонку("СлужебнаяНач");
   ТЗИтог.УдалитьКолонку("СлужебнаяКон");
   Возврат ТЗИтог;
КонецФункции
16 Злобный Йожег
 
10.12.09
20:18
Сделать запрос к исходной таблице значений, и в запросе указать раздел итоги, куда расписать всю иерархию колонок. Результат запроса выгрузить:

ДеревоЗнач = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
17 luns
 
10.12.09
22:14
18 jk3
 
11.12.09
00:48
(15) да, это оно. БОЛЬШОЕ спасибо!
остальное завтра посмотрю
19 jk3
 
18.12.09
14:58
В итоге получилось вот что


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

   Перем ТЗИтог, ТЗДляПередачиВРекурсивныйВызов; // явно определяем переменные, чтобы не было косяков с рекурсией

   Если ЭтоПервыйУровеньИерархии = 1 Тогда
       // сворачиваем таблицу только 1 раз в начале, этого достаточно, чтобы на всех остальных
       // уровнях иерархии она тоже всегда была свернута
       // алгоритм будет правильно работать и без этой строки (все-равно таблица будет сворачиваться для вычисления сумм),
       // но таким образом мы уменьшаем размер таблиц, передаваемых в рекурсивные вызовы
       ТЗ.Свернуть(СтрКолонкиГрупп, СтрКолонкиСумм);
       ТЗ.Сортировать(СтрКолонкиГрупп); // нужно обязательно для правильно расстановки маркеров (см. ниже)
       
       // т.к. это значение нельзя задать как параметр по-умолчанию, инициализируем его здесь
       Если СтруктураВнутр = "" Тогда
           СтруктураВнутр = ЗначениеВСтрокуВнутр(СоздатьОбъект("ТаблицаЗначений"));
       КонецЕсли;
   КонецЕсли;

   // выгружаем исходную ТЗ в итоговую, которую будем сворачивать
   ТЗ.Выгрузить(ТЗИтог);
   
   // определяем текущую и оставшиеся колонки групп
   ПозицияПервойЗапятой = Найти(СтрКолонкиГрупп, ",");
   
   Если ПозицияПервойЗапятой > 0 Тогда
       СтрТекущаяКолонкаГрупп = СокрЛП(Лев(СтрКолонкиГрупп, ПозицияПервойЗапятой - 1));
       СтрОставшиесяКолонкиГрупп = СокрЛП(Сред(СтрКолонкиГрупп, ПозицияПервойЗапятой + 1));
   Иначе
       СтрТекущаяКолонкаГрупп = СокрЛП(СтрКолонкиГрупп);
       СтрОставшиесяКолонкиГрупп = "";
   КонецЕсли;

   // сворачиваем итоговую таблицу по текущей колонке, получая суммы
   ТЗИтог.Свернуть(СтрТекущаяКолонкаГрупп, СтрКолонкиСумм);
   
   Если СтрОставшиесяКолонкиГрупп = "" Тогда
       // это последний уровень иерархии, нужно всем элементам проставить таблицы значений
       ТЗИтог.НоваяКолонка("ТаблицаЗначений");
       ТЗИтог.ВыбратьСтроки();
       Пока ТЗИтог.ПолучитьСтроку() = 1 Цикл
           ТЗИтог.ТаблицаЗначений = ЗначениеИзСтрокиВнутр(СтруктураВнутр);
       КонецЦикла;
       
       Возврат ТЗИтог;
   КонецЕсли;

   // если получилась пустая таблица, то дальше делать нечего
   Если ТЗИтог.КоличествоСтрок() = 0 Тогда
       Возврат ТЗИтог;
   КонецЕсли;
   
   // расставляем маркеры начала и конца значений в исходной ТЗ по текущей группируемой колонке
   ТЗИтог.НоваяКолонка("ТаблицаЗначений");
   ТЗИтог.НоваяКолонка("СлужебнаяНач", "Число");
   ТЗИтог.НоваяКолонка("СлужебнаяКон", "Число");
   
   ТЗИтог.ВыбратьСтроки();
   Пока ТЗИтог.ПолучитьСтроку() = 1 Цикл
       НомерСтроки_ = 0;
       ТЗ.НайтиЗначение(ТЗИтог.ПолучитьЗначение(ТЗИтог.НомерСтроки, СтрТекущаяКолонкаГрупп), НомерСтроки_, СтрТекущаяКолонкаГрупп);

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


и добавил функцию вычисления к полученному дереву общего итого (при желании её можно интегрировать в функцию создания дерева)


Функция ДобавитьВДеревоИтого(ТЗ, СтрКолонкиГрупп, СтрКолонкиСумм)
   
   ТЗРезультат = СоздатьОбъект("ТаблицаЗначений");

   // преобразуем переданные строки с колонками через запятую в список наименований колонок для удобства
   СтрКолонки = СтрКолонкиГрупп;
   СписокКолонокГрупп = СоздатьОбъект("СписокЗначений");
   
   Пока 1 = 1 Цикл
       ПозицияПервойЗапятой = Найти(СтрКолонки, ",");
       
       Если ПозицияПервойЗапятой > 0 Тогда
           СписокКолонокГрупп.ДобавитьЗначение(СокрЛП(Лев(СтрКолонки, ПозицияПервойЗапятой - 1)));
           СтрКолонки = СокрЛП(Сред(СтрКолонки, ПозицияПервойЗапятой + 1));
       Иначе
           СписокКолонокГрупп.ДобавитьЗначение(СокрЛП(СтрКолонки));
           Прервать;
       КонецЕсли;
   КонецЦикла;
   
   СтрКолонки = СтрКолонкиСумм;
   СписокКолонокСумм = СоздатьОбъект("СписокЗначений");
   
   Пока 1 = 1 Цикл
       ПозицияПервойЗапятой = Найти(СтрКолонки, ",");
       
       Если ПозицияПервойЗапятой > 0 Тогда
           СписокКолонокСумм.ДобавитьЗначение(СокрЛП(Лев(СтрКолонки, ПозицияПервойЗапятой - 1)));
           СтрКолонки = СокрЛП(Сред(СтрКолонки, ПозицияПервойЗапятой + 1));
       Иначе
           СписокКолонокСумм.ДобавитьЗначение(СокрЛП(СтрКолонки));
           Прервать;
       КонецЕсли;
   КонецЦикла;
   
   // теперь по полученным спискам формируем колонки и заполняем суммы
   Для индекс1 = 1 По СписокКолонокГрупп.РазмерСписка() Цикл
       ТЗРезультат.НоваяКолонка(СписокКолонокГрупп.ПолучитьЗначение(индекс1));
   КонецЦикла;

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

   ТЗРезультат.НоваяКолонка("ТаблицаЗначений");
   ТЗРезультат.ТаблицаЗначений = ТЗ;
   
   Возврат ТЗРезультат;
   
КонецФункции // ДобавитьВДеревоИтого