Работа с Деревом значений в 1С отличается от работы с обычной таблицей из-за иерархической структуры. Основная сложность заключается в том, что количество уровней вложенности заранее может быть неизвестно. В этой статье мы разберем, как эффективно перебирать все уровни дерева и как правильно удалять из него данные, не нарушая целостность коллекции.
Для того чтобы обработать каждую строку дерева, независимо от того, на каком уровне она находится, чаще всего используется рекурсия. Это метод, при котором процедура вызывает саму себя для обработки подчиненных строк. Рассмотрим классический пример реализации такого обхода:
Процедура ОбходДереваРекурсивно(КоллекцияСтрок)
Для Каждого ТекущаяСтрока Из КоллекцияСтрок Цикл
// Выполняем полезные действия со строкой
Сообщить(ТекущаяСтрока.Номенклатура);
// Если у строки есть подчиненные строки, вызываем эту же процедуру
Если ТекущаяСтрока.Строки.Количество() > 0 Тогда
ОбходДереваРекурсивно(ТекущаяСтрока.Строки);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Чтобы запустить этот процесс, в основной части кода нужно передать корневую коллекцию строк вашего дерева:
ОбходДереваРекурсивно(ДеревоОстатков.Строки);
Важный момент: если дерево очень глубокое (сотни уровней), рекурсия может привести к переполнению стека. В таких редких случаях вместо рекурсии используют массив (стек), в который помещают строки для обработки в цикле Пока.
Если ваша задача заключается только в поиске строк по определенному значению колонки, в 1С предусмотрен штатный метод НайтиСтроки. В отличие от аналогичного метода в таблице значений, у дерева значений этот метод ищет данные сразу на всех уровнях иерархии.
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Удалить", Истина);
// Метод вернет массив строк, найденных на любом уровне вложенности
МассивСтрокДляУдаления = Дерево.Строки.НайтиСтроки(ПараметрыОтбора, Истина);
Использование этого метода избавляет от написания сложных циклов, если вам нужно просто получить список объектов по фильтру.
Типичная ошибка при удалении строк из дерева — попытка удалить строку напрямую через корневую коллекцию Дерево.Строки.Удалить(Строка). Этот метод сработает только для строк первого уровня. Если строка находится глубже, платформа выдаст ошибку или действие не будет выполнено, так как строка принадлежит коллекции своего родителя, а не корню дерева.
У каждой строки дерева значений есть свойство Родитель. Оно указывает на вышестоящую строку. Если свойство имеет значение Неопределено, значит, перед нами строка верхнего уровня. Мы можем использовать это для универсального удаления:
Процедура УдалитьСтрокуДерева(ВыбраннаяСтрока, Дерево)
Родитель = ВыбраннаяСтрока.Родитель;
Если Родитель = Неопределено Тогда
// Удаляем из корня дерева
Дерево.Строки.Удалить(ВыбраннаяСтрока);
Иначе
// Удаляем из коллекции родителя
Родитель.Строки.Удалить(ВыбраннаяСтрока);
КонецЕсли;
КонецПроцедуры
Если вы предварительно собрали массив строк на удаление (например, через НайтиСтроки или рекурсивный обход), нужно учитывать два правила:
Рассмотрим пример безопасного удаления из массива:
Для Каждого СтрНаУдаление Из МассивНаУдаление Цикл
// Проверяем, не была ли строка удалена ранее вместе с родителем
Если СтрНаУдаление.Владелец() = Неопределено Тогда
Продолжить;
КонецЕсли;
// Определяем коллекцию, из которой нужно удалить
Коллекция = ?(СтрНаУдаление.Родитель = Неопределено, Дерево.Строки, СтрНаУдаление.Родитель.Строки);
Коллекция.Удалить(СтрНаУдаление);
КонецЦикла;
Иногда, если дерево содержит тысячи строк и нужно удалить большую их часть, эффективнее не удалять строки по одной, а создать новое дерево. Проанализируем этот сценарий: вы можете использовать метод Скопировать() для создания пустой структуры дерева, а затем рекурсивно переносить в него только те строки, которые соответствуют вашим условиям. Это может работать быстрее, так как операция удаления в сложных иерархиях ресурсозатратна.
Также помните о методе Очистить(). Его можно применять к коллекции Строки любого узла. Это мгновенно удаляет всю ветку подчиненных элементов, что гораздо эффективнее, чем перебор и удаление каждой дочерней строки по отдельности.