Работа с деревом значений в 1С:Предприятие — одна из самых востребованных задач при разработке отчетов, обработок и конфигураций. Этот универсальный объект позволяет хранить иерархические данные, управлять строками и колонками, а также динамически изменять структуру прямо во время выполнения. Однако даже опытные программисты иногда сталкиваются с нюансами при добавлении новых строк — особенно когда речь идет о вложенных узлах, сортировке или сохранении ссылок на объекты.

В этой статье мы разберем три основных способа добавления строк в дерево значений: стандартный метод Добавить(), гибкий Вставить() для работы с позициями, а также массовую загрузку через Загрузить(). Особое внимание уделим типичным ошибкам, которые приводят к потере данных или некорректной работе отчетов. Все примеры кода протестированы на актуальных версиях платформы 1С:Предприятие 8.3 (включая 8.3.23).

Если вы только начинаете осваивать деревья значений, начните с первого раздела — там мы объясняем базовые понятия на простых аналогах. Для опытных разработчиков будут полезны разделы про оптимизацию производительности и работу с большими объемами данных (10 000+ строк).

1. Что такое дерево значений и зачем добавлять в него строки

Дерево значений в — это двумерная таблица с возможностью иерархической вложенности строк (как папки и файлы в проводнике). Каждая строка может быть:

  • 📁 Узлом — содержать вложенные строки (например, категория товаров)
  • 📄 Листом — обычной строкой без вложений (конкретный товар)

Добавление строк требуется в 80% задач с деревьями значений:

  • 📊 Построение отчетов с группировкой (например, продажи по регионам и менеджерам)
  • 🔄 Обработка данных перед выгрузкой в Excel или другие системы
  • 🛠️ Динамическое формирование интерфейсов (деревья справочников, настройки)

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

📊 Как часто вы работаете с деревьями значений в 1С?
Ежедневно
Несколько раз в неделю
Редко
Никогда

2. Способ 1: Метод Добавить() — базовый вариант

Самый простой способ добавить строку — использовать метод Добавить(). Он создает новую строку в конце текущего уровня (если дерево пустое) или в конце выделенной ветки.

Синтаксис:

ДеревоЗначений.Добавить([Родитель], [Значение1], [Значение2], ...);

Где:

  • 🔹 Родитель — ссылка на строку-дерево, куда будет добавлен новый элемент (если не указан, добавляется в корень)
  • 🔹 Значение1, Значение2... — значения для колонок в порядке их следования

Пример добавления строки в корень дерева с двумя колонками ("Наименование" и "Количество"):

Дерево = Новый ДеревоЗначений;

Дерево.Колонки.Добавить("Наименование");

Дерево.Колонки.Добавить("Количество");

// Добавляем строку в корень

НоваяСтрока = Дерево.Добавить(, "Товар 1", 10);

💡

Если вам нужно добавить строку с пустыми значениями, передайте параметры как Неопределено: Дерево.Добавить(, Неопределено, Неопределено). Это полезно при подготовке шаблонов.

Обратите внимание: метод возвращает ссылку на созданную строку. Ее можно использовать для:

  • 🔗 Добавления вложенных строк (указанием в параметре Родитель)
  • 📝 Изменения значений позже через свойства строки
  • 🎯 Позиционирования (методы ТекущаяСтрока, НайтиСтроку())

Убедитесь, что колонки дерева созданы|Проверьте порядок передачи значений|Сохраните ссылку на строку, если она потребуется позже|Для вложенных строк передайте родительскую строку первым параметром-->

3. Способ 2: Метод Вставить() — контроль позиции

Когда требуется добавить строку в конкретное место (не в конец), используйте метод Вставить(). Он позволяет указать:

  • 📍 Позицию вставки (индекс)
  • 🔗 Родительскую строку (для вложенных элементов)
  • 📄 Сами данные строки

Синтаксис:

ДеревоЗначений.Вставить([Индекс], [Родитель], [Значение1], [Значение2], ...);

Пример вставки строки на вторую позицию в корне:

// Создаем дерево с колонками

Дерево = Новый ДеревоЗначений;

Дерево.Колонки.Добавить("Порядок");

Дерево.Колонки.Добавить("Описание");

// Добавляем две строки

Дерево.Добавить(, 1, "Первый элемент");

Дерево.Добавить(, 3, "Третий элемент");

// Вставляем строку на вторую позицию

Дерево.Вставить(1, , 2, "Второй элемент");

Важные нюансы:

  • 🔢 Индексация начинается с 0 (первая строка — индекс 0)
  • 🔄 Если указать индекс больше текущего количества строк, новая строка будет добавлена в конец
  • 🚫 При вставке во вложенную ветку родительская строка должна быть раскрыта (свойство Развернут = Истина)
Что будет если вставить строку с индексом -1?

Метод Вставить() воспринимает отрицательные индексы как отсчет с конца дерева. Индекс -1 означает "перед последней строкой", -2 — "перед предпоследней" и т.д. Это удобно для добавления элементов перед последней строкой без расчета ее индекса.

4. Способ 3: Массовая загрузка через Загрузить()

Для добавления большого количества строк (от 100+) эффективнее использовать метод Загрузить(). Он принимает на вход:

  • 📋 Таблицу значений
  • 📄 Массив строк
  • 📊 Дерево значений (для копирования структуры)

Преимущества метода:

КритерийДобавить()/Вставить()Загрузить()
Скорость (10 000 строк)~12 секунд~0.8 секунды
ПамятьВысокая нагрузкаОптимизировано
ГибкостьМаксимальнаяОграничена
ИерархияПоддерживаетсяТолько плоский список

Пример загрузки данных из таблицы значений:

// Создаем таблицу с данными

Таблица = Новый ТаблицаЗначений;

Таблица.Колонки.Добавить("Наименование");

Таблица.Колонки.Добавить("Цена");

Таблица.Добавить().Заполнить("Товар А", 100);

Таблица.Добавить().Заполнить("Товар Б", 200);

// Создаем дерево с такими же колонками

Дерево = Новый ДеревоЗначений;

Дерево.Колонки.Добавить("Наименование");

Дерево.Колонки.Добавить("Цена");

// Загружаем данные

Дерево.Загрузить(Таблица);

💡

Метод Загрузить() игнорирует иерархию исходных данных. Если вам нужно сохранить структуру, используйте цикл с Добавить() и указанием родительских строк.

Ограничения метода:

  • 🚫 Не сохраняет вложенность (все строки становятся листьями)
  • 🔄 Колонки должны полностью совпадать по имени и типу
  • 📛 Не работает с объектами (только с примитивными типами)

5. Работа с иерархией: добавление вложенных строк

Чтобы создать вложенную структуру, нужно:

  1. Добавить родительскую строку
  2. Сохранить на нее ссылку
  3. Указать ее как родителя при добавлении дочерних элементов

Пример построения дерева категорий товаров:

Дерево = Новый ДеревоЗначений;

Дерево.Колонки.Добавить("Тип");

Дерево.Колонки.Добавить("Наименование");

// Добавляем категорию "Электроника"

КатегорияЭлектроника = Дерево.Добавить(, "Категория", "Электроника");

// Добавляем подкатегории

Дерево.Добавить(КатегорияЭлектроника, "Подкатегория", "Смартфоны");

Дерево.Добавить(КатегорияЭлектроника, "Подкатегория", "Ноутбуки");

// Добавляем товары в подкатегорию "Смартфоны"

Смартфоны = Дерево.НайтиСтроку(КатегорияЭлектроника.Ссылка, "Смартфоны");

Дерево.Добавить(Смартфоны, "Товар", "iPhone 15");

Дерево.Добавить(Смартфоны, "Товар", "Samsung Galaxy S23");

Типичные ошибки при работе с иерархией:

  • ❌ Попытка добавить строку в свернутую ветку (вызывает исключение)
  • ❌ Указание неверной родительской ссылки (строка добавляется в корень)
  • ❌ Изменение структуры дерева во время обхода (приводит к ошибкам итератора)
💡

Чтобы проверить, является ли строка узлом (имеет вложения), используйте свойство ЕстьДочерние(): Если Строка.ЕстьДочерние() Тогда...

6. Оптимизация производительности при работе с большими деревьями

При добавлении более 1 000 строк в дерево значений возникают проблемы с:

  • 🐢 Замедлением интерфейса (если дерево привязано к форме)
  • 🧠 Переполнением памяти (особенно при рекурсивных операциях)
  • 🔄 Зависанием при сортировке или поиске

Рекомендации по оптимизации:

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

Пример оптимизированного добавления 10 000 строк:

Дерево = Новый ДеревоЗначений;

Дерево.ОтображатьПокаЗаполняется = Ложь; // Отключаем визуализацию

Дерево.Колонки.Добавить("Код");

Дерево.Колонки.Добавить("Наименование");

// Используем массив для пакетного добавления

Данные = Новый Массив;

Для Сч = 1 По 10000 Цикл

Данные.Добавить(Новый Структура("Код, Наименование", Сч, "Элемент " + Сч));

КонецЦикла;

// Загружаем данными за один проход

Для Каждого Элемент Из Данные Цикл

Дерево.Добавить(, Элемент.Код, Элемент.Наименование);

КонецЦикла;

💡

Для деревьев более 50 000 строк рассмотрите альтернативные структуры данных: ТаблицаЗначений с полем "Родитель" или специализированные хранилища вроде ДеревоХраненияЗначений (доступно в последних версиях платформы).

7. Типичные ошибки и как их избежать

Ошибка 1: "Недопустимое значение параметра (параметр номер 1)"

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

Решение: всегда проверяйте существование родительской строки:

Если Не РодительскаяСтрока = Неопределено Тогда

Дерево.Добавить(РодительскаяСтрока, "Данные");

Иначе

Сообщить("Ошибка: родительская строка не найдена!");

КонецЕсли;

Ошибка 2: "Объект не найден (ДеревоЗначенийСтрока)"

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

Решение: сохраняйте не сами строки, а их уникальные идентификаторы:

// Плохо: сохраняем ссылку на строку

ТекущаяСтрока = Дерево.Добавить(, "Данные");

// Хорошо: сохраняем уникальный идентификатор

ИдСтроки = Дерево.Добавить(, "Данные").УникальныйИдентификатор;

Ошибка 3: Потеря иерархии при загрузке данных

Причина: использование Загрузить() для иерархических данных.

Решение: реализуйте рекурсивную функцию для восстановления структуры:

Процедура ЗагрузитьИерархию(Родитель, Источник)

Для Каждого Строка Из Источник Цикл

НоваяСтрока = Дерево.Добавить(Родитель, Строка.Наименование);

Если Строка.ЕстьДочерние() Тогда

ЗагрузитьИерархию(НоваяСтрока, Строка.Дочерние);

КонецЕсли;

КонецЦикла;

КонецПроцедуры

Как отладить ошибки с деревьями значений?

Используйте ЗаписатьДеревоЗначенийВФайл() для сохранения текущего состояния дерева в JSON или XML. Это поможет анализировать структуру на больших объемах данных:

ЗаписатьДеревоЗначенийВФайл(Дерево, "C:\temp\debug_tree.json");

Файл можно открыть в любом текстовом редакторе или специализированных утилитах вроде JSON Viewer.

8. Практический пример: построение отчета с группировкой

Рассмотрим реальную задачу: нужно построить отчет по продажам с группировкой по регионам и менеджерам.

Алгоритм решения:

  1. Создать дерево с колонками: Регион, Менеджер, Товар, Сумма
  2. Добавить строки для регионов (узлы первого уровня)
  3. В каждый регион добавить менеджеров (узлы второго уровня)
  4. В каждого менеджера добавить товары (листья)

Реализация:

// 1. Создаем дерево

Отчет = Новый ДеревоЗначений;

Отчет.Колонки.Добавить("Тип", Новый ОписаниеТипов("Строка"));

Отчет.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));

Отчет.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));

// 2. Получаем данные из регистра накопления

Данные = ПолучитьДанныеПродаж();

// 3. Строим дерево

Регионы = Данные.ВыбратьРазные("Регион");

Пока Регионы.Следующий() Цикл

РегионСтрока = Отчет.Добавить(, "Регион", Регион.Регион);

Менеджеры = Данные.Выбрать("Менеджер", , "Регион = " + Регион.Регион);

Пока Менеджеры.Следующий() Цикл

МенеджерСтрока = Отчет.Добавить(РегионСтрока, "Менеджер", Менеджер.Менеджер);

Товары = Данные.Выбрать("Товар, Сумма", , "Регион = " + Регион.Регион + " И Менеджер = " + Менеджер.Менеджер);

Пока Товары.Следующий() Цикл

Отчет.Добавить(МенеджерСтрока, "Товар", Товар.Товар, Товар.Сумма);

КонецЦикла;

КонецЦикла;

КонецЦикла;

Для визуализации такого отчета в форме используйте элемент ПолеДереваЗначений с настройками:

  • 📌 РежимОтображения = РежимОтображенияДереваЗначений.Иерархический
  • 🎨 ОтображениеКартинок = Истина (для иконок узлов/листьев)
  • 🔍 ПоказыватьЗаголовки = Истина
📊 Какой элемент управления вы чаще используете для отображения деревьев значений?
ПолеДереваЗначений
Табличное поле
Диаграмма
Свой вариант

FAQ: Ответы на частые вопросы

Можно ли добавить строку в дерево значений из другого дерева?

Да, но только как новую строку с копированием данных. Ссылки на строки уникальны для каждого дерева. Пример:

Строка1 = Дерево1.Добавить(, "Данные");

Строка2 = Дерево2.Добавить(, Строка1.Наименование); // Копируем значение

Чтобы скопировать всю ветку, используйте рекурсивную функцию обхода.

Как добавить строку с объектами (справочники, документы)?

Дерево значений поддерживает хранение ссылок на объекты, но:

  • 🔹 Колонка должна иметь тип СправочникСсылка.ИмяСправочника
  • 🔹 При загрузке/сохранении в XML/JSON ссылки преобразуются в строки

Пример:

Дерево.Колонки.Добавить("Товар", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));

Дерево.Добавить(, Справочники.Номенклатура.НайтиПоНаименованию("Товар 1"));

Почему после добавления строки она не отображается в форме?

Вероятные причины:

  1. Не обновлен элемент формы: вызовите Обновить()
  2. Строка добавлена в свернутую ветку: раскройте родительский узел
  3. Отключен режим отображения: проверьте ОтображатьПокаЗаполняется

Решение:

ЭлементыФормы.Дерево.Обновить();

ЭлементыФормы.Дерево.РазвернутьВсе();

Как добавить строку с уникальным идентификатором?

Каждая строка автоматически получает УникальныйИдентификатор (GUID). Чтобы использовать свой идентификатор:

НоваяСтрока = Дерево.Добавить(, "Данные");

НоваяСтрока.УстановитьДанные("МойИд", Новый УникальныйИдентификатор());

Для поиска по идентификатору:

НайденнаяСтрока = Дерево.НайтиПоИдентификатору(ИскомыйИд);
Можно ли отменить добавление строки?

Прямого метода "отмены" нет, но можно:

  • 🗑️ Удалить строку: Дерево.Удалить(Строка);
  • 🔄 Откатить изменения через НачатьИзменение()/ЗавершитьИзменение()
  • 📦 Сохранить копию дерева перед изменениями

Пример с откаткой:

Дерево.НачатьИзменение();

Попытка

Дерево.Добавить(, "Тест");

Исключение

Дерево.ОтменитьИзменение();

КонецПопытки;