Как отсортировать коллекцию в 1С без использования Таблицы Значений

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

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

Решение 1: Сортировка на стороне клиента с использованием Списка Значений и Соответствия

Рассмотрим ситуацию, когда нам необходимо отсортировать массив структур прямо на клиенте, не прибегая к вызову серверного контекста. Это может быть критично для производительности интерфейса. Один из наиболее оригинальных способов — использование комбинации объектов СписокЗначений и Соответствие.

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


Функция ОтсортироватьКоллекциюНаКлиенте(Коллекция, ИмяПоляСортировки, Направление = "Возр") Экспорт
    
    НаправлениеСортировки = ?(Направление = "Возр", НаправлениеСортировки.Возр, НаправлениеСортировки.Убыв);
    
    // Используем соответствие для группировки элементов с одинаковыми значениями ключа
    ДанныеСортировки = Новый Соответствие;
    СписокКлючей = Новый СписокЗначений;
    
    // Специальный массив для обработки значений Неопределено (т.к. Неопределено не может быть ключом соответствия)
    МассивНеопределено = Новый Массив;
    
    Для Каждого Элемент Из Коллекция Цикл
        ЗначениеКлюча = Элемент[ИмяПоляСортировки];
        
        Если ЗначениеКлюча = Неопределено Тогда
            МассивНеопределено.Добавить(Элемент);
            Продолжить;
        КонецЕсли;
        
        СписокЭлементов = ДанныеСортировки[ЗначениеКлюча];
        Если СписокЭлементов = Неопределено Тогда
            СписокЭлементов = Новый Массив;
            ДанныеСортировкиВставить(ЗначениеКлюча, СписокЭлементов);
            СписокКлючей.Добавить(ЗначениеКлюча);
        КонецЕсли;
        СписокЭлементов.Добавить(Элемент);
    КонецЦикла;
    
    // Используем штатный метод СпискаЗначений
    СписокКлючей.СортироватьПоЗначению(НаправлениеСортировки);
    
    Результат = Новый Массив;
    
    // Если нужно, чтобы Неопределено было в начале (при возрастании)
    Если Направление = "Возр" Тогда
        Для Каждого Эл Из МассивНеопределено Цикл
            Результат.Добавить(Эл);
        КонецЦикла;
    КонецЕсли;
    
    Для Каждого ЭлементСписка Из СписокКлючей Цикл
        Ключ = ЭлементСписка.Значение;
        СписокЭлементов = ДанныеСортировки[Ключ];
        Для Каждого Эл Из СписокЭлементов Цикл
            Результат.Добавить(Эл);
        КонецЦикла;
    КонецЦикла;
    
    // Если нужно, чтобы Неопределено было в конце (при убывании)
    Если Направление <> "Возр" Тогда
        Для Каждого Эл Из МассивНеопределено Цикл
            Результат.Добавить(Эл);
        КонецЦикла;
    КонецЕсли;
    
    Возврат Результат;
КонецФункции

Разберем этот код подробнее. Мы создаем СписокЗначений, в который помещаем только те данные, по которым будет идти сравнение. Объект Соответствие позволяет нам быстро найти исходную структуру по значению ключа. Важный нюанс: если в коллекции несколько элементов имеют одинаковое значение поля сортировки, мы храним их в массиве внутри соответствия, чтобы не потерять данные.

Решение 2: Традиционный подход через серверную функцию и временную ТЗ

Если специфика задачи позволяет выполнить серверный вызов, самым надежным методом остается использование ТаблицыЗначений. Рассмотрим, как сделать это максимально универсально. Проанализируем пример функции, которая принимает любую коллекцию и возвращает отсортированный массив:


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

Обратим внимание на важный момент оптимизации. Если ваша коллекция — это простой Массив простых значений (строк или чисел), вы можете ускорить процесс заполнения таблицы, используя метод ЗагрузитьКолонку(). Проанализируем, как это сокращает код:


// Пример быстрой загрузки массива в ТЗ
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Значение");
// Создаем нужное количество строк заранее
Для н = 1 По ИсходныйМассив.Количество() Цикл
    ТЗ.Добавить();
КонецЦикла;
// Загружаем данные одной операцией
ТЗ.ЗагрузитьКолонку(ИсходныйМассив, "Значение");
ТЗ.Сортировать("Значение Возр");
Результат = ТЗ.ВыгрузитьКолонку("Значение");

Решение 3: Современный метод Сортировать() для массивов (Платформа 8.3.24+)

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

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


// Массив структур
МассивДанных = Новый Массив;
МассивДанных.Добавить(Новый Структура("Имя, Возраст", "Иван", 25));
МассивДанных.Добавить(Новый Структура("Имя, Возраст", "Анна", 22));

// Сортировка по полю "Возраст" по возрастанию
МассивДанных.Сортировать("Возраст Возр");

Преимущества данного метода:

  1. Высокая производительность, так как сортировка выполняется на уровне ядра платформы.
  2. Доступность как на клиенте, так и на сервере.
  3. Возможность сортировки по нескольким полям (через запятую).
  4. Отсутствие необходимости создавать вспомогательные объекты.

Решение 4: Использование объекта ТекстовыйДокумент (для простых строк)

В некоторых специфических задачах, когда нужно отсортировать большой объем текстовых данных на клиенте, можно применить объект ТекстовыйДокумент. Рассмотрим этот "лайфхак":


ТекстДок = Новый ТекстовыйДокумент;
Для Каждого СтрокаДанных Из МассивСтрок Цикл
    ТекстДок.ДобавитьСтроку(СтрокаДанных);
КонецЦикла;

// Встроенный метод сортировки текстовых строк
ТекстДок.Сортировать();

ОтсортированныйМассив = Новый Массив;
Для н = 1 По ТекстДок.КоличествоСтрок() Цикл
    ОтсортированныйМассив.Добавить(ТекстДок.ПолучитьСтроку(н));
КонецЦикла;

Этот способ эффективен только для одномерных массивов строк, но он работает значительно быстрее, чем самописный алгоритм "пузырька" на языке 1С.

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

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

Рекомендация: Если требуется гарантированная сортировка по реквизитам ссылочных объектов (Наименование, Артикул, Дата документа), всегда выполняйте эту операцию на сервере через запрос или ТаблицуЗначений.

Выводы и рекомендации

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

← На главную