Как подсчитать количество строк в дереве значений 1С по родителю и условию?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Работа с иерархическими структурами данных, такими как ДеревоЗначений, требует от разработчика понимания того, как устроены коллекции строк на разных уровнях вложенности. В отличие от обычной таблицы, дерево состоит из узлов, каждый из которых может содержать собственную коллекцию строк. Часто возникает задача: как быстро и эффективно узнать количество строк, относящихся к определенному «родителю», особенно если нужно учитывать только те строки, где установлено определенное значение (например, флаг в булевой колонке).

В этой статье мы подробно разберем несколько способов решения этой задачи: от самых простых встроенных методов до рекурсивных функций и оптимизированных подходов для больших объемов данных.

Способ 1. Использование свойства Количество() для текущего уровня

Если нам необходимо узнать общее число строк, которые находятся непосредственно «внутри» конкретной строки-родителя (на один уровень ниже), мы можем воспользоваться стандартным свойством коллекции Строки. Рассмотрим ситуацию, когда у нас уже есть объект типа ДанныеФормыДеревоЗначенийСтрока или СтрокаДереваЗначений.

Проанализируем пример кода:


// Предположим, ПеременнаяРодитель — это конкретная строка дерева
КоличествоДочернихСтрок = ПеременнаяРодитель.Строки.Количество();
Сообщить("У данной строки " + КоличествоДочернихСтрок + " прямых потомков.");

Важный нюанс: этот метод возвращает количество строк только на следующем уровне иерархии. Если у дочерних строк есть свои «дети» (внуки для исходной строки), они в этот расчет не попадут. Это самый быстрый способ, так как система просто возвращает размер массива строк текущего узла.

Способ 2. Подсчет строк с условием (тип Булево) через цикл

Зачастую простого количества строк недостаточно. Представим, что в дереве есть колонка Выбран (тип Булево), и нам нужно посчитать, сколько строк отмечено галочкой внутри конкретного родителя. Поскольку стандартный метод Итог() в ДеревеЗначений работает преимущественно с числовыми типами, для булевых значений нам придется реализовать обход.

Разберем, как это сделать с помощью простого цикла:


КоличествоОтмеченных = 0;
Для Каждого ТекущаяСтрока Из ПеременнаяРодитель.Строки Цикл
    Если ТекущаяСтрока.Выбран Тогда
        КоличествоОтмеченных = КоличествоОтмеченных + 1;
    КонецЕсли;
КонецЦикла;
// Результат можно записать в специальную колонку родителя
ПеременнаяРодитель.КоличествоДляОтображения = КоличествоОтмеченных;

Этот способ нагляден и прост в отладке. Мы перебираем коллекцию Строки нашего родителя и инкрементируем счетчик при выполнении условия.

Способ 3. Использование метода НайтиСтроки() для быстрой фильтрации

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

Выясним, как применить этот метод для нашей задачи:


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

Преимущество этого метода в том, что если мы установим второй параметр в значение Истина, платформа самостоятельно выполнит рекурсивный поиск по всей глубине иерархии ниже указанной строки. Это избавляет нас от необходимости писать сложные рекурсивные алгоритмы вручную.

Способ 4. Рекурсивный подсчет всех вложенных строк

Если нам нужно посчитать абсолютно все строки во всей ветке (всех уровней вложенности), а метод НайтиСтроки по каким-то причинам не подходит (например, нужно сложное условие), мы можем создать рекурсивную функцию. Рассмотрим пример такой функции:


Функция ПосчитатьВсеСтрокиРекурсивно(ТекущаяСтрока)
    Всего = ТекущаяСтрока.Строки.Количество();
    Для Каждого ПодчиненнаяСтрока Из ТекущаяСтрока.Строки Цикл
        Всего = Всего + ПосчитатьВсеСтрокиРекурсивно(ПодчиненнаяСтрока);
    КонецЦикла;
    Возврат Всего;
КонецФункции

В этом примере функция вызывает саму себя для каждой дочерней строки, суммируя результаты. Это позволяет «спуститься» до самых нижних листьев дерева.

Способ 5. Оптимизация через служебную колонку и метод Итог()

Для профессиональной разработки, особенно если дерево содержит тысячи строк, рекомендуется использовать «трюк» со служебной числовой колонкой. Как мы помним, метод Итог() не суммирует булевы значения, но он отлично суммирует числа по всей иерархии.

Проанализируем алгоритм реализации:

  1. Добавим в ДеревоЗначений скрытую колонку СчетчикДляИтога (тип Число).
  2. При изменении значения в колонке Выбран, устанавливаем в СчетчикДляИтога единицу (1), если выбрано, и ноль (0), если флаг снят.
  3. Для получения общего количества отмеченных строк по всему дереву или по ветке используем: ОбщийИтог = Дерево.Строки.Итог("СчетчикДляИтога", Истина);.

Этот метод работает на уровне платформенных механизмов и выполняется значительно быстрее, чем ручной перебор строк в цикле Для Каждого.

Способ 6. Использование запросов для больших деревьев

В исключительных случаях, когда ДеревоЗначений представляет собой огромный массив данных, программный перебор может вызвать зависание интерфейса. В такой ситуации мы можем временно выгрузить дерево (или его часть) в ТаблицуЗначений, поместить ее во временную таблицу запроса и выполнить агрегатную функцию КОЛИЧЕСТВО(*) с группировкой по нужным полям.

Резюмируем:

Выбор конкретного метода зависит от объема ваших данных и того, насколько часто требуется обновлять результат подсчета. В большинстве повседневных задач 1С метод НайтиСтроки с параметром рекурсии является оптимальным балансом между скоростью разработки и производительностью системы.

← На главную