Разработка и Конфигурация · 11 мин
Как объединить два дерева значений в 1С
Как объединить два дерева значений в 1С: методы слияния, обход строк, копирование реквизитов, проверка результата и типичные ошибки при работе с ДеревоЗначений.
Дерево значений в 1С — это иерархическая структура данных, где у каждой строки могут быть подчинённые строки произвольной вложенности. На практике часто возникает задача объединить два таких дерева: например, свести иерархию подразделений из двух баз, объединить деревья остатков по складам или слить иерархические справочники перед выгрузкой. Штатного метода «Слить» у объекта ДеревоЗначений нет — объединение выполняется программным обходом.
Способ объединения выбирается исходя из смысла задачи. Иногда нужно просто дописать строки одного дерева в конец другого. Чаще требуется сопоставить строки по ключу (коду, ссылке, наименованию) и при совпадении объединить значения колонок, а при отсутствии — добавить новую ветку. Именно второй сценарий встречается в учётных задачах чаще всего.
Ниже разобраны оба подхода с готовым кодом на встроенном языке, объяснены ограничения платформы, способы проверки результата и типичные ошибки, которые приводят к потере строк или задвоению веток.
Что нужно определить перед объединением
Прежде чем писать код, ответьте на три вопроса. Первый — совпадает ли состав колонок у обоих деревьев. Если колонки различаются, в приёмнике нужно заранее добавить недостающие колонки через Колонки.Добавить(), иначе при копировании значений возникнет ошибка обращения к несуществующему свойству.
Второй вопрос — что считать «одинаковой» строкой. Обычно это ключевая колонка: код элемента, ссылка на справочник, уникальный идентификатор. Объединение по наименованию рискованно: в разных источниках наименования часто отличаются пробелами, регистром и сокращениями, что приведёт к задвоению веток.
Третий вопрос — что делать при совпадении ключей: суммировать числовые колонки (остатки, суммы), перезаписывать значения из второго дерева или оставлять значения первого. От этого зависит логика ветки «строка найдена» в коде слияния.
Как выбрать способ объединения
Подготовка структуры колонок
Если деревья получены из разных источников (например, из запросов к разным регистрам), состав колонок почти наверняка отличается. Перед слиянием пройдите по колонкам второго дерева и добавьте отсутствующие в первое:
Для Каждого Колонка Из Дерево2.Колонки Цикл
Если Дерево1.Колонки.Найти(Колонка.Имя) = Неопределено Тогда
Дерево1.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
КонецЕсли;
КонецЦикла;
Обратите внимание на типы: если в одном дереве колонка числовая, а в другом — строковая, суммирование даст ошибку или неожиданный результат. Проверьте типы ключевых и числовых колонок заранее, при необходимости приведите значения через функции преобразования.
Простое добавление веток без сопоставления
Самый простой сценарий — когда строки второго дерева гарантированно не пересекаются с первым и их нужно добавить целиком, сохранив иерархию. Для этого пишется рекурсивная процедура копирования:
Процедура СкопироватьСтроки(СтрокиИсточника, СтрокиПриёмника)
Для Каждого Строка Из СтрокиИсточника Цикл
НоваяСтрока = СтрокиПриёмника.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
СкопироватьСтроки(Строка.Строки, НоваяСтрока.Строки);
КонецЦикла;
КонецПроцедуры
// Вызов на верхнем уровне:
СкопироватьСтроки(Дерево2.Строки, Дерево1.Строки);
ЗаполнитьЗначенияСвойств копирует значения одноимённых колонок, поэтому состав колонок деревьев должен совпадать или быть заранее выровнен. Рекурсия обеспечивает копирование вложенности любой глубины.
Такой подход используют, например, при объединении деревьев из филиалов, где каждая ветка уникальна, или при сборке дерева настроек из нескольких шаблонов.
- 1Проверьте, что состав и типы колонок обоих деревьев совпадают
- 2Напишите рекурсивную процедуру копирования строк
- 3Вызовите её для коллекций Строки верхнего уровня
- 4Проверьте итоговое количество строк и глубину вложенности
Слияние с сопоставлением строк по ключу
Основной рабочий сценарий — объединение деревьев, описывающих одни и те же объекты: например, остатки по номенклатуре из двух складских учётов, где иерархия построена по группам товаров. Здесь на каждом уровне нужно искать строку с тем же ключом и либо объединять значения, либо добавлять новую ветку.
Процедура СлитьСтроки(СтрокиИсточника, СтрокиПриёмника)
Для Каждого Строка Из СтрокиИсточника Цикл
Найденная = СтрокиПриёмника.Найти(Строка.Код, "Код", Ложь);
Если Найденная = Неопределено Тогда
Найденная = СтрокиПриёмника.Добавить();
ЗаполнитьЗначенияСвойств(Найденная, Строка);
Иначе
Найденная.Сумма = Найденная.Сумма + Строка.Сумма;
Найденная.Количество = Найденная.Количество + Строка.Количество;
КонецЕсли;
СлитьСтроки(Строка.Строки, Найденная.Строки);
КонецЦикла;
КонецПроцедуры
Ключевой момент — параметр Ложь в методе Найти(): он отключает поиск во вложенных строках, и поиск выполняется только на текущем уровне. Это важно, потому что один и тот же ключ может встречаться на разных уровнях иерархии, а сопоставлять нужно строки одного уровня под одним родителем.
Если ключ составной (например, группа + склад), метод Найти не подойдёт — используйте перебор строк текущего уровня с проверкой обоих условий или предварительно сформируйте соответствие «ключ → строка» для быстрого поиска.
Найденная = СтрокиПриёмника.Найти(Строка.Код, "Код", Ложь); // поиск только на текущем уровнеАльтернатива: объединение через таблицу значений
Когда деревья большие и сопоставление по ключу выполняется часто, попарный поиск строк становится узким местом. Практичная альтернатива — развернуть оба дерева в плоские таблицы значений (сохранив признак уровня или идентификатор родителя), объединить их в одну таблицу, выполнить свёртку или группировку, а затем построить дерево заново.
Преимущество подхода — возможность использовать запрос к таблице значений с группировкой и итогами, что работает заметно быстрее циклического поиска. Недостаток — нужно хранить связь «строка — родитель», чтобы корректно восстановить иерархию. Обычно для этого добавляют колонки с уникальным идентификатором строки и идентификатором родителя, а при построении дерева используют соответствие для быстрого поиска родительской строки.
Этот вариант оправдан при объёме в десятки тысяч строк и выше. Для небольших деревьев (сотни — единицы тысяч строк) рекурсивное слияние проще в отладке и достаточно быстро.
Сравнение подходов
Простой и наглядный код; подходит для деревьев до нескольких тысяч строк. На больших объёмах поиск по каждой строке замедляет работу.
Быстрее на больших объёмах благодаря группировке запросом; требует хранения связей строка-родитель и повторного построения иерархии.
Самый быстрый вариант, но применим только когда строки деревьев не пересекаются по ключам.
Проверка результата и контрольные точки
После объединения обязательно проверьте результат, иначе ошибка в логике сопоставления всплывёт уже в отчёте или выгрузке. Минимальный набор проверок: контрольные суммы по числовым колонкам, количество строк верхнего уровня, отсутствие дублей ключей на одном уровне.
Контрольные суммы считают до и после слияния: сумма по колонке «Сумма» в объединённом дереве должна равняться сумме аналогичных итогов двух исходных деревьев (с учётом того, что итоги по родительским строкам могут дублировать детальные — считайте либо только листовые строки, либо только верхний уровень). Расхождение означает потерю строк или двойной учёт.
Для поиска задвоенных веток пройдите по каждому уровню и проверьте уникальность ключей через соответствие: если ключ уже встречался на этом уровне под тем же родителем — сопоставление сработало неверно.
Как проверить результат
Итог объединённого дерева равен сумме итогов исходных
Итог меньше (потеряны строки) или больше (двойной учёт)
Ключ встречается один раз под одним родителем
Дубли ключей — сопоставление не сработало
Вложенность соответствует исходным деревьям
Ветки «переехали» на другой уровень или потеряли подчинённые строки
Типичные ошибки и ограничения
Самая частая ошибка — поиск с включённым флагом рекурсии (Истина в четвёртом параметре Найти). Тогда строка находится на другом уровне, и подчинённые строки присоединяются не туда, ломая иерархию. Вторая типичная проблема — сравнение ключей разных типов: строка «001» и число 1 не равны, поэтому сопоставление молча не срабатывает и ветки дублируются.
Отдельно стоит упомянуть ссылки из разных баз: ссылки на справочник из одной информационной базы никогда не равны ссылкам из другой, даже если это «тот же» элемент. При обмене между базами сопоставляйте по уникальному идентификатору, коду или специальному ключу синхронизации.
Ограничение платформы: дерево значений — несериализуемый между вызовами клиент-сервер объект в ряде контекстов, поэтому слияние тяжёлых деревьев выполняйте на сервере, передавая на клиент только результат для отображения.
Частые вопросы
Можно ли объединить деревья значений без программирования?
Нет, штатной команды объединения деревьев в конфигураторе и типовых механизмах нет. В пользовательском режиме деревья (например, в отчётах на СКД) иногда можно свести настройкой группировок, но полноценное слияние двух программно сформированных деревьев требует кода на встроенном языке.
Как объединить деревья, если ключевые колонки называются по-разному?
Добавьте в процедуру слияния параметры с именами колонок-ключей источника и приёмника и обращайтесь к значениям через квадратные скобки: Строка[ИмяКолонкиИсточника]. Либо заранее переименуйте колонки одного из деревьев, приведя структуру к единому виду.
Почему после слияния дерево показывает не все строки на форме?
Если дерево привязано к элементу формы, после программного изменения проверьте, что обновлён именно реквизит формы, а не локальная переменная. Также убедитесь, что новые колонки добавлены и в элементы формы — иначе данные есть, но не отображаются.
Как объединить три и больше деревьев?
Последовательно слейте их в одно дерево-приёмник: первое дерево берётся за основу, остальные по очереди передаются в ту же рекурсивную процедуру слияния. Логика сопоставления и суммирования от количества источников не меняется.
Что делать, если в деревьях одинаковые ключи на разных уровнях иерархии?
Используйте составной ключ: сопоставляйте не только значение колонки, но и родителя строки. Практичный способ — искать строку только среди подчинённых конкретного родителя (как в примере с Найти по текущему уровню), тогда одинаковые ключи под разными родителями корректно останутся отдельными ветками.
Что важно запомнить
Деревья значений в 1С объединяются только программно: рекурсивным обходом с копированием строк или со сопоставлением по ключевой колонке на каждом уровне. Перед слиянием выровняйте состав и типы колонок, при поиске отключайте рекурсию в Найти(), а после — сверяйте контрольные суммы и уникальность ключей. Для больших объёмов эффективнее объединение через таблицу значений с группировкой и повторным построением иерархии.