Как преобразовать плоскую таблицу значений в дерево значений в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки часто возникает задача преобразовать линейную таблицу, содержащую ссылки на родителей и потомков, в иерархическую структуру — ДеревоЗначений — для этого подойдёт инструментарий разработчика для анализа метаданных. Это необходимо для отображения данных в виде дерева на формах или при формировании сложных отчетов. Давайте разберем, как сделать это эффективно, используя рекурсивные алгоритмы.

Подготовка исходных данных

Для начала нам потребуется таблица, содержащая как минимум две колонки: Потомок (идентификатор элемента) и Родитель (идентификатор родительского узла). Если у элемента нет родителя, мы считаем его корневым элементом дерева.

Рекурсивный подход к построению дерева

Основная идея рекурсии заключается в последовательном поиске всех потомков для текущего узла. Как только мы находим потомков, мы добавляем их в ветку текущего родителя и запускаем ту же процедуру для найденных элементов. Рассмотрим оптимизированный вариант такого алгоритма.

Разберем классическую реализацию процедуры:


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

Оптимизация процесса обработки

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

Давайте проанализируем ключевые моменты оптимизации:

  1. Исключение лишнего поиска: Передавая в функцию ссылку на коллекцию Строки текущего узла, мы избавляемся от необходимости перебирать всё дерево при каждой итерации.
  2. Правильная передача параметров: В процедуре важно использовать Знач, если мы хотим обезопасить передаваемые данные от случайных изменений внутри рекурсивного вызова.
  3. Обработка корневых узлов: Корневыми считаются те строки, у которых Родитель не найден в колонке Потомок (или равен 0/Неопределено). Инициализировать рекурсию нужно именно с них.

Важные рекомендации по безопасности кода

При работе с рекурсивными алгоритмами всегда стоит учитывать следующие аспекты:

Зацикливание: Если в ваших данных есть логическая ошибка (например, элемент А подчинен Б, а Б подчинен А), возникнет бесконечная рекурсия, которая приведет к ошибке переполнения стека. Рекомендуем перед выполнением алгоритма проверять данные на отсутствие циклических ссылок.

Производительность: Если исходная таблица содержит тысячи строк, метод НайтиСтроки может работать медленно. В таких случаях мы рекомендуем:

Мы проанализировали процесс построения дерева и выяснили, что грамотно написанная рекурсивная процедура позволяет чисто и компактно решить поставленную задачу. Главное — помнить об эффективности поиска подчиненных элементов и правильной передаче ссылок на коллекции строк. Для этой задачи есть конструктор визуализации деревьев на формах 1С.

← На главную