Разработка и Конфигурация · 9 мин
Как создать дерево значений в 1С: пошаговая инструкция
Как создать дерево значений в 1С: добавление колонок и строк, обход дерева, поиск, вывод в табличное поле и отличия от таблицы значений. Примеры кода.
Дерево значений в 1С — это объект встроенного языка, предназначенный для хранения иерархических данных: номенклатуры по группам, структуры организации, плана счетов с субсчетами, многоуровневых спецификаций. В отличие от таблицы значений, каждая строка дерева может содержать собственные подчинённые строки, что позволяет описывать вложенность любой глубины.
Объект создаётся программно конструктором Новый ДеревоЗначений и существует только в оперативной памяти сеанса. Он не сохраняется в базе данных сам по себе — для постоянного хранения данные нужно переносить в справочники, регистры или хранилище значений. Типичный сценарий: заполнить дерево из запроса с итогами по иерархии, обработать строки и вывести результат пользователю в табличное поле формы.
Материал рассчитан на разработчиков и продвинутых пользователей, работающих с конфигуратором. Все примеры приведены на встроенном языке 1С и подходят для платформы 8.3, как для управляемых, так и для обычных форм — с учётом различий, о которых сказано отдельно.
Создание дерева значений и добавление колонок
Первый шаг — объявить переменную и создать объект. Конструктор не принимает параметров, структура дерева определяется позже через коллекцию колонок. Каждая колонка имеет имя, тип и, при необходимости, заголовок для отображения.
Дерево = Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));
Дерево.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Дерево.Колонки.Добавить("Ссылка");
Если тип не указан, колонка принимает значения произвольного типа — это удобно для ссылок на разные виды объектов, но замедляет сортировку и может привести к ошибкам сравнения. Для колонок, участвующих в итогах и поиске, тип лучше задавать явно через ОписаниеТипов.
Заполнение дерева строками
Строки верхнего уровня добавляются методом Дерево.Строки.Добавить(). Метод возвращает объект новой строки, у которого есть собственная коллекция Строки — через неё создаются подчинённые уровни. Глубина вложенности не ограничена.
Группа = Дерево.Строки.Добавить();
Группа.Наименование = "Материалы";
Элемент = Группа.Строки.Добавить();
Элемент.Наименование = "Стальной лист 2 мм";
Элемент.Количество = 10;
На практике дерево чаще заполняется не вручную, а из запроса. Для этого у объекта запроса есть метод Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией) — он сразу возвращает готовое дерево значений, построенное по иерархии справочника. Это самый быстрый способ получить вложенную структуру без рекурсивного кода.
Запрос = Новый Запрос("ВЫБРАТЬ Номенклатура.Ссылка КАК Ссылка ИЗ Справочник.Номенклатура КАК Номенклатура ИТОГИ ПО Ссылка ТОЛЬКО ИЕРАРХИЯ");
Дерево = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
- 1Создайте объект: Дерево = Новый ДеревоЗначений
- 2Добавьте колонки с типами через Дерево.Колонки.Добавить()
- 3Добавьте строки верхнего уровня через Дерево.Строки.Добавить()
- 4У каждой строки-родителя вызовите Строки.Добавить() для дочерних элементов
- 5Проверьте результат выводом в сообщение или табличное поле
Обход дерева и поиск строк
Для обработки всех уровней применяется рекурсивная процедура: она перебирает строки текущего уровня и вызывает сама себя для каждой строки, у которой есть подчинённые элементы. Это стандартный приём, без него обработать дерево произвольной глубины невозможно.
Процедура ОбойтиДерево(Строки)
Для Каждого Строка Из Строки Цикл
Сообщить(Строка.Наименование);
ОбойтиДерево(Строка.Строки);
КонецЦикла;
КонецПроцедуры
Для поиска на одном уровне используется метод Строки.Найти(Значение, "ИмяКолонки"). Он не спускается в подчинённые уровни, поэтому для поиска по всему дереву снова нужна рекурсия либо предварительное преобразование дерева в таблицу значений с колонкой родителя. Также полезны методы Родитель и Уровень() строки — они позволяют определить положение элемента в иерархии.
Функция НайтиСтрокуРекурсивно(Строки, ИскомоеЗначение)
Для Каждого Строка Из Строки Цикл
Если Строка.Ссылка = ИскомоеЗначение Тогда
Возврат Строка;
КонецЕсли;
Найденная = НайтиСтрокуРекурсивно(Строка.Строки, ИскомоеЗначение);
Если Найденная <> Неопределено Тогда
Возврат Найденная;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункцииВывод дерева на форму
В обычных формах дерево значений напрямую связывается с табличным полем: элементу формы назначается тип «Дерево значений», и иерархия отображается автоматически с возможностью сворачивания узлов. В управляемых формах прямой привязки нет — дерево нужно поместить в реквизит формы соответствующего типа.
Типовой приём для управляемой формы: создать реквизит формы типа «ДеревоЗначений» программно через ИзменитьРеквизиты() или декларативно в конфигураторе, затем перенести данные методом ЗначениеВРеквизитФормы(Дерево, РеквизитДерево). Обратное преобразование выполняется через РеквизитФормыВЗначение().
Дерево значений против таблицы значений
Выбор между двумя объектами определяется структурой данных. Если записи равноправны и не имеют вложенности — достаточно таблицы значений: она быстрее заполняется, поддерживает индексы, свёртку и удобную сортировку. Дерево оправдано только там, где есть реальная иерархия и нужны операции с родителями и потомками.
Сравнение вариантов
Подходит для плоских списков: строк документа, результатов запроса без иерархии. Поддерживает индексы, Свернуть(), НайтиСтроки(). Не хранит вложенность.
Подходит для иерархических данных: групп справочников, многоуровневых спецификаций, структуры подразделений. Не поддерживает индексы и свёртку напрямую.
Подходит, когда данные нужно только прочитать и вывести. Не требует ручного построения дерева, но результат сложнее модифицировать программно.
Типичные ошибки и ограничения
Наиболее частая проблема — попытка обратиться к колонке, которая не добавлена, или опечатка в имени колонки. Встроенный язык не проверяет имена колонок на этапе написания кода, ошибка проявляется только при выполнении. Вторая типичная ситуация — путаница между коллекцией Строки дерева и коллекцией Строки конкретной строки: добавление «не туда» даёт неверную иерархию.
Ещё одно ограничение: дерево значений не поддерживает методы Свернуть() и индексы, привычные по таблице значений. Если нужны итоги по уровням, их рассчитывают вручную в рекурсивном обходе либо получают сразу из запроса с группировками.
Как проверить результат
У строк верхнего уровня заполнены подчинённые коллекции Строки там, где ожидается вложенность
Все строки оказались на одном уровне — добавление шло не в ту коллекцию
Табличное поле показывает узлы со значками сворачивания
Показан плоский список или пустая форма — проверьте тип реквизита и вызов ЗначениеВРеквизитФормы
Рекурсивная процедура обработала все уровни, счётчик совпадает с ожидаемым числом строк
Обработана только часть строк — в обходе пропущен рекурсивный вызов
Часто задаваемые вопросы
Чем дерево значений отличается от таблицы значений?
Таблица значений — плоский набор строк с колонками, удобный для списков и итогов. Дерево значений добавляет каждой строке собственную коллекцию подчинённых строк, что позволяет хранить иерархию произвольной глубины и работать с парами «родитель — потомок».
Можно ли сохранить дерево значений в базе данных?
Напрямую нет — объект существует только в памяти сеанса. Для хранения дерево сериализуют через ЗначениеВСтрокуВнутр() или помещают в хранилище значения реквизита объекта. Чаще иерархию хранят естественным образом — в иерархическом справочнике.
Как быстро заполнить дерево из справочника?
Используйте запрос с конструкцией ИТОГИ ПО Ссылка ТОЛЬКО ИЕРАРХИЯ и выгрузите результат методом Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией). Платформа сама построит вложенную структуру без ручного обхода.
Почему возникает ошибка при передаче дерева между клиентом и сервером?
В управляемом приложении дерево значений не сериализуется при клиент-серверных вызовах. Решение — хранить дерево в реквизите формы типа ДеревоЗначений и работать с ним через ЗначениеВРеквизитФормы и РеквизитФормыВЗначение.
Как найти строку на любом уровне дерева?
Метод Найти() ищет только в пределах одной коллекции строк. Для поиска по всему дереву напишите рекурсивную функцию, которая проверяет строки текущего уровня и вызывает себя для каждой подчинённой коллекции, возвращая найденную строку или Неопределено.
Как подсчитать итоги по уровням дерева?
Метода Свернуть() у дерева нет. Итоги рассчитывают в рекурсивном обходе: функция возвращает сумму по дочерним строкам, а родительская строка аккумулирует результаты. Альтернатива — получить итоги сразу в запросе с группировками по иерархии.
Что важно запомнить
Дерево значений создаётся конструктором, наполняется колонками и вложенными строками, обходится рекурсией. Для управляемых форм его помещают в реквизит формы, для плоских данных выгоднее таблица значений, а иерархию из базы проще всего получить запросом с итогами по иерархии.