В разработке на управляемых формах программисты часто сталкиваются с ситуацией, когда необходимо отсортировать небольшой объем данных прямо на клиенте, не прибегая к серверному вызову. Типичный пример — получение списка файлов во временную таблицу и выборка последних десяти по дате изменения. Однако штатный метод Сортировать() объекта ТаблицаЗначений недоступен на тонком клиенте, а коллекции типа ДанныеФормыКоллекция (реквизиты формы) при вызове сортировки неявно обращаются к серверу, что может негативно сказаться на производительности и отзывчивости интерфейса.
В этой статье мы подробно разберем, почему возникает эта проблема, и рассмотрим несколько эффективных способов ее решения: от использования штатного СписокЗначений до реализации собственных алгоритмов сортировки массивов.
Разберем фундаментальную причину: в управляемом приложении объект ТаблицаЗначений является чисто серверным типом. На стороне клиента мы работаем с его проекцией — ДанныеФормыКоллекция. Хотя визуально они похожи, у клиентской коллекции отсутствует метод Сортировать(). Если же мы пытаемся создать ТаблицаЗначений программно на клиенте (используя различные обходные пути), платформа либо выдаст ошибку, либо выполнит неявную сериализацию данных на сервер, произведет там сортировку и вернет результат обратно. Наша задача — избежать этого лишнего трафика.
Самый простой и «легальный» способ отсортировать данные на клиенте — воспользоваться объектом СписокЗначений. В отличие от таблиц, этот объект полностью доступен на клиенте и обладает встроенными методами сортировки. Рассмотрим, как это работает на практике.
Проанализируем алгоритм действий:
СписокЗначений.Значение помещаем данные, по которым будем сортировать (например, ДатаИзменения), а в поле Представление или СвязанныйОбъект — сами данные или структуру с описанием файла.СортироватьПоЗначению() или СортироватьПоПредставлению().Пример реализации на клиенте:
&НаКлиенте
Процедура ОтсортироватьФайлыНаКлиенте(МассивФайлов)
СписокДляСортировки = Новый СписокЗначений;
Для Каждого Файл Из МассивФайлов Цикл
// Помещаем дату в значение для корректного сравнения типов
// В представление записываем имя или путь
СписокДляСортировки.Добавить(Файл.ДатаИзменения, Файл.ПолноеИмя);
КонецЦикла;
// Сортируем по дате (по значению) в порядке убывания
СписокДляСортировки.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
// Получаем результат (например, первые 10)
Для Индекс = 0 По Мин(9, СписокДляСортировки.Количество() - 1) Цикл
Элемент = СписокДляСортировки[Индекс];
Сообщить("Файл: " + Элемент.Представление + " Дата: " + Элемент.Значение);
КонецЦикла;
КонецПроцедуры
Если данные имеют сложную структуру и использование СписокЗначений кажется неудобным, мы можем реализовать классический алгоритм сортировки для обычного Массив. Рассмотрим наиболее популярный (хотя и не самый быстрый для огромных массивов) алгоритм «пузырька». Он идеально подходит для задач, где количество элементов не превышает нескольких сотен.
Проанализируем код процедуры, которая сортирует массив структур по заданному ключу:
&НаКлиенте
Процедура СортироватьМассивСтруктур(МассивСтруктур, Знач ИмяКлюча, ОбратнаяСортировка = Ложь)
ДлинаМассива = МассивСтруктур.Количество();
Для ПервыйСчетчик = 0 По ДлинаМассива - 1 Цикл
ПорядокИзменен = Ложь;
Для ВторойСчетчик = 0 По ДлинаМассива - ПервыйСчетчик - 2 Цикл
ЛеваяСтруктура = МассивСтруктур[ВторойСчетчик];
ПраваяСтруктура = МассивСтруктур[ВторойСчетчик + 1];
ЛевоеЗначение = ЛеваяСтруктура[ИмяКлюча];
ПравоеЗначение = ПраваяСтруктура[ИмяКлюча];
НадоПереставить = ?(ОбратнаяСортировка, ЛевоеЗначение < ПравоеЗначение, ЛевоеЗначение > ПравоеЗначение);
Если НадоПереставить Тогда
// Меняем элементы местами
МассивСтруктур[ВторойСчетчик] = ПраваяСтруктура;
МассивСтруктур[ВторойСчетчик + 1] = ЛеваяСтруктура;
ПорядокИзменен = Истина;
КонецЕсли;
КонецЦикла;
// Если за проход ни одной перестановки не было — массив отсортирован
Если Не ПорядокИзменен Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Выясним преимущества этого метода: он не требует создания дополнительных объектов 1С, работает в оперативной памяти клиента и позволяет гибко управлять логикой сравнения (например, добавлять дополнительные условия при равенстве дат).
Иногда логика получения данных всё равно требует обращения к серверу. В этом случае эффективнее выполнить сортировку там, используя мощь ТаблицаЗначений или даже запрос к временной таблице. Рассмотрим ситуацию автора темы: получение списка ярлыков через COM-объект WScript.Shell на клиенте и последующую фильтрацию.
Разберем шаги совместной работы клиента и сервера:
Массив из Структура.&НаСервереБезКонтекста.ТаблицаЗначений, используем быстрый метод Сортировать().Посмотрим на пример реализации такой связки:
&НаСервереБезКонтекста
Функция Получить10ПоследнихФайловНаСервере(МассивДанных)
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТЗ.Колонки.Добавить("Путь", Новый ОписаниеТипов("Строка"));
// Заполняем таблицу данными из массива
Для Каждого СтрДанных Из МассивДанных Цикл
НоваяСтрока = ТЗ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрДанных);
КонецЦикла;
// Используем штатную быструю сортировку
ТЗ.Сортировать("Дата УБЫВ");
Результат = Новый Массив;
Количество = Мин(ТЗ.Количество(), 10);
Для Итератор = 0 По Количество - 1 Цикл
Результат.Добавить(Новый Структура("Дата, Путь", ТЗ[Итератор].Дата, ТЗ[Итератор].Путь));
КонецЦикла;
Возврат Результат;
КонецФункции
Выбор метода сортировки на клиенте зависит от объема данных и требований к чистоте кода:
СписокЗначений с методом СортироватьПоЗначению().&НаСервереБезКонтекста, так как встроенные методы ТаблицаЗначений написаны на C++ и работают значительно быстрее любого кода на встроенном языке 1С.Важно помнить: попытки «обмануть» систему и создать ТаблицаЗначений на клиенте через ОписаниеТипов являются недокументированной особенностью и могут привести к нестабильной работе программы в веб-клиенте или будущих обновлениях платформы.