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