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

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

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

Проблема контекстных вызовов и типы данных

Для начала проанализируем ситуацию: почему стандартный метод ДанныеФормыВЗначение() требует контекста? Дело в том, что этот метод работает с данными текущей формы, которые должны быть полностью переданы на сервер для воссоздания объекта. Если мы хотим избежать передачи контекста, нам необходимо использовать директиву &НаСервереБезКонтекста, но в такую процедуру нельзя передать объект типа ДанныеФормыДерево напрямую в качестве параметра.

Следовательно, у нас есть два пути: либо выполнять сортировку полностью на клиенте, используя встроенные механизмы коллекций данных формы, либо передавать данные в "облегченном" виде (например, через массив структур) на сервер без контекста. Мы подробно рассмотрим клиентские методы, так как они наиболее эффективны для средних объемов данных.

Решение №1: Реализация «гномьей» сортировки на клиенте

Рассмотрим первый вариант — реализацию алгоритма сортировки на встроенном языке 1С. Одним из наиболее простых в реализации для коллекций является алгоритм "гномьей сортировки" (Gnome sort). Он удобен тем, что использует только метод Сдвинуть(), который доступен для элементов коллекции на клиенте.

Проанализируем программный код универсальной процедуры для дерева:


&НаКлиентеНаСервереБезКонтекста
Процедура СортироватьДанныеФормыДерево(КоллекцияСортировки, КолонкаСортировки, ВключатьПодчиненные = Ложь)
    
    // Получаем элементы текущего уровня дерева
    КоллекцияСтрок = КоллекцияСортировки.ПолучитьЭлементы();
    
    // Вызываем вспомогательную процедуру сортировки для коллекции
    СортироватьДанныеФормыКоллекция(КоллекцияСтрок, КолонкаСортировки);
    
    // Если требуется рекурсия по подчиненным узлам
    Если ВключатьПодчиненные Тогда
        Для Каждого ТекущаяСтрока Из КоллекцияСтрок Цикл
            СортироватьДанныеФормыДерево(ТекущаяСтрока, КолонкаСортировки, ВключатьПодчиненные);
        КонецЦикла;
    КонецЕсли;
    
КонецПроцедуры

&НаКлиентеНаСервереБезКонтекста
Процедура СортироватьДанныеФормыКоллекция(КоллекцияСтрок, КолонкаСортировки)
    
    ПоследнийИндекс = КоллекцияСтрок.Количество() - 1;
    ТекущийИндекс = 1;
    
    Пока ТекущийИндекс <= ПоследнийИндекс Цикл
        
        Если ТекущийИндекс = 0 Тогда
            ТекущийИндекс = 1;
        КонецЕсли;
        
        ТекущаяСтрока = КоллекцияСтрок[ТекущийИндекс];
        ПрошлаяСтрока = КоллекцияСтрок[ТекущийИндекс - 1];
        
        // Сравниваем значения в колонке сортировки
        Если ТекущаяСтрока[КолонкаСортировки] < ПрошлаяСтрока[КолонкаСортировки] Тогда
            // Если порядок нарушен, сдвигаем строку вверх
            КоллекцияСтрок.Сдвинуть(ТекущийИндекс, -1);
            ТекущийИндекс = ТекущийИндекс - 1;
        Иначе
            // Если порядок верный, идем к следующему элементу
            ТекущийИндекс = ТекущийИндекс + 1;
        КонецЕсли;
        
    КонецЦикла;
    
КонецПроцедуры

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

Решение №2: Оптимизированная сортировка через «СписокЗначений»

Теперь рассмотрим более продвинутый и быстрый метод. Выясним причину, почему он эффективнее: платформа 1С предоставляет объект СписокЗначений, метод СортироватьПоЗначению() которого реализован на уровне ядра системы (C++). Это работает значительно быстрее, чем любой цикл на встроенном языке.

Разберем алгоритм:

  1. Обходим коллекцию строк и выгружаем значения колонки сортировки в СписокЗначений.
  2. В качестве Значения элемента списка помещаем само значение колонки, а в качестве Представления или дополнительного соответствия — ссылку на саму строку дерева.
  3. Сортируем СписокЗначений.
  4. Передвигаем строки в коллекции ДанныеФормыДерево в соответствии с новым порядком в списке.

&НаКлиентеНаСервереБезКонтекста
Процедура СортироватьДанныеФормыКоллекцияОптимизировано(КоллекцияСтрок, КолонкаСортировки)
    
    // Используем Соответствие для связи значения и массива строк (на случай дублей)
    ПараметрыЗначений = Новый Соответствие;
    СортированныеЗначения = Новый СписокЗначений;
    
    Для Каждого ТекущаяСтрока Из КоллекцияСтрок Цикл
        
        ТекущееЗначение = ТекущаяСтрока[КолонкаСортировки];
        
        МассивСтрокСЭтимЗначением = ПараметрыЗначений.Получить(ТекущееЗначение);
        Если МассивСтрокСЭтимЗначением = Неопределено Тогда
            МассивСтрокСЭтимЗначением = Новый Массив;
            ПараметрыЗначений.Вставить(ТекущееЗначение, МассивСтрокСЭтимЗначением);
            СортированныеЗначения.Добавить(ТекущееЗначение);
        КонецЕсли;
        
        МассивСтрокСЭтимЗначением.Добавить(ТекущаяСтрока);
        
    КонецЦикла;
    
    // Выполняем быструю системную сортировку
    СортированныеЗначения.СортироватьПоЗначению(НаправлениеСортировки.Возр);
    
    НовыйИндекс = 0;
    Для Каждого ЭлементСписка Из СортированныеЗначения Цикл
        
        МассивСтрок = ПараметрыЗначений.Получить(ЭлементСписка.Значение);
        Для Каждого ТекущаяСтрока Из МассивСтрок Цикл
            
            ТекущийИндекс = КоллекцияСтрок.Индекс(ТекущаяСтрока);
            ШагСдвига = НовыйИндекс - ТекущийИндекс;
            
            Если НЕ ШагСдвига = 0 Тогда
                КоллекцияСтрок.Сдвинуть(ТекущийИндекс, ШагСдвига);
            КонецЕсли;
            
            НовыйИндекс = НовыйИндекс + 1;
            
        КонецЦикла;
        
    КонецЦикла;
    
КонецПроцедуры

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

Технические нюансы и рекомендации

Проанализируем несколько важных моментов, которые стоит учитывать при реализации подобных механизмов:

1. Сохранение идентификаторов строк. При использовании метода Сдвинуть(), внутренние идентификаторы строк ДанныеФормыЭлементДерева.ПолучитьИдентификатор() сохраняются. Это критически важно, если у вас есть связанные данные или текущая строка выделена пользователем. Фокус не пропадет, и связанные обработчики будут работать корректно.

2. Рекурсия и уровни вложенности. Для полной сортировки всего дерева необходимо комбинировать метод из Решения №2 с рекурсивным обходом, как показано в Решении №1. Помните, что рекурсия в 1С имеет ограничение по глубине, но для обычных бизнес-задач (деревья глубиной 5–10 уровней) это не является проблемой.

3. Когда все же стоит уйти на сервер? Если ваше дерево содержит десятки тысяч строк, даже клиентская сортировка может вызвать "замирание" интерфейса из-за большого количества вызовов Сдвинуть(). В этом случае рассмотрим альтернативный вариант:

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

4. Сортировка по нескольким колонкам. Если вам нужно сортировать дерево по нескольким полям (например, "Дата" + "Приоритет"), при заполнении СписокЗначений формируйте ключ сортировки как составную строку или число, обеспечивающее правильный порядок при сравнении.

Таким образом, мы рассмотрели, как эффективно управлять порядком строк в ДанныеФормыДерево. Использование клиентской сортировки через СписокЗначений является оптимальным балансом между скоростью разработки и производительностью приложения, позволяя пользователю мгновенно видеть результат без ожидания пересылки данных по сети.

← На главную