Как выполнить естественную сортировку строк с числами в таблице значений 1С 8.3?

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

При разработке отчетов и обработок в среде 1С:Предприятие 8 часто возникает задача упорядочивания строк в таблице значений. Если колонка содержит текстовые данные с номерами (например, 1.Кабинет, 5.Кабинет, 11.Кабинет), стандартный метод Сортировать() выстраивает их в неожиданном для пользователя порядке: 1.Кабинет, затем 11.Кабинет и только потом 5.Кабинет.

Выясним причину такого поведения платформы и подробно разберем практические способы реализации правильной «естественной» сортировки (Natural Sort).

Почему стандартная сортировка строк работает именно так

Метод ТаблицаЗначений.Сортировать() выполняет строго лексикографическое (посимвольное по кодам Unicode) сравнение строк. При сравнении строк 11.Кабинет и 5.Кабинет платформа сравнивает первые символы: символ 1 имеет меньший код, чем символ 5, поэтому строка 11.Кабинет оказывается выше в списке.

В платформе 1С:Предприятие встроенного механизма естественного упорядочивания строк без предварительной подготовки данных нет. Объект СравнениеЗначений также не предоставляет пользовательских функций-компараторов для строк. Поэтому упорядочивание выполняется несколькими проверенными способами.

Способ 1. Добавление вспомогательной числовой колонки

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

Разберем по шагам реализацию данного алгоритма:

  1. Добавляем в ТаблицаЗначений служебную колонку Порядок с типом Число.
  2. Обходим строки таблицы и выделяем числовую подстроку до разделителя (точки).
  3. Заполняем служебную колонку числовым значением.
  4. Вызываем метод Сортировать() по новой колонке.
  5. Удаляем служебную колонку, если она больше не нужна.

Посмотрим на пример программного кода:


// Добавляем служебную числовую колонку
ТЗ.Колонки.Добавить("Порядок", Новый ОписаниеТипов("Число"));

Для Каждого Стр Из ТЗ Цикл
    ПозицияТочки = Найти(Стр.Кабинет, ".");
    Если ПозицияТочки > 0 Тогда
        НомерСтрокой = СокрЛП(Лев(Стр.Кабинет, ПозицияТочки - 1));
        Стр.Порядок = Число(НомерСтрокой);
    Иначе
        Стр.Порядок = 0;
    КонецЕсли;
КонецЦикла;

// Выполняем сортировку по числовому полю
ТЗ.Сортировать("Порядок Возр");

// При необходимости удаляем временную колонку
ТЗ.Колонки.Удалить("Порядок");

Способ 2. Использование функций БСП для конфигураций 1С

Если разработка ведется в типовой конфигурации (например, ЗУП 3.1, ERP 2, УТ 11, БП 3.0), проанализируем возможности библиотеки стандартных подсистем (БСП). Общий модуль СтроковыеФункцииКлиентСервер содержит готовые инструменты для парсинга и форматирования строк.

Вариант с разложением строки в массив подстрок:


ТЗ.Колонки.Добавить("Порядок", Новый ОписаниеТипов("Число"));

Для Каждого Стр Из ТЗ Цикл
    ЧастиСтроки = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(Стр.Кабинет, ".");
    Если ЧастиСтроки.Количество() > 0 Тогда
        Стр.Порядок = Число(ЧастиСтроки[0]);
    КонецЕсли;
КонецЦикла;

ТЗ.Сортировать("Порядок Возр");
ТЗ.Колонки.Удалить("Порядок");

Способ 3. Нормализация строковых номеров (дополнение ведущими нулями или пробелами)

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

С помощью БСП дополнение строки выполняется вызовом одной функции:


ТЗ.Колонки.Добавить("СтрокаДляСортировки", Новый ОписаниеТипов("Строка"));

Для Каждого Стр Из ТЗ Цикл
    ПозицияТочки = Найти(Стр.Кабинет, ".");
    Если ПозицияТочки > 0 Тогда
        Номер = СокрЛП(Лев(Стр.Кабинет, ПозицияТочки - 1));
        Остаток = Сред(Стр.Кабинет, ПозицияТочки);
        // Приводим номер к фиксированной длине в 5 знаков
        Стр.СтрокаДляСортировки = СтроковыеФункцииКлиентСервер.ДополнитьСтроку(Номер, 5, "0", "Слева") + Остаток;
    Иначе
        Стр.СтрокаДляСортировки = Стр.Кабинет;
    КонецЕсли;
КонецЦикла;

ТЗ.Сортировать("СтрокаДляСортировки Возр");
ТЗ.Колонки.Удалить("СтрокаДляСортировки");

Для сложных номеров с несколькими уровнями иерархии (например, 1.12.3, 10.2.1) можно использовать алгоритмическую нормализацию всех составных частей:


Функция ИсправитьНомерДляСортировки(Знач Текст, МинДлинаРаздела = 5) Экспорт
    Результат = "";
    Разделители = " ,.;-\/:*";
    БылРазделитель = Истина;
    ТекущийБлок = "";
    
    Для Сч = 1 По СтрДлина(Текст) Цикл
        Символ = Сред(Текст, Сч, 1);
        Если Найти(Разделители, Символ) > 0 Тогда
            Если Не БылРазделитель Тогда
                ДлинаБлока = СтрДлина(ТекущийБлок);
                Если ДлинаБлока < МинДлинаРаздела Тогда
                    ТекущийБлок = Лев("          ", МинДлинаРаздела - ДлинаБлока) + ТекущийБлок;
                КонецЕсли;
                Результат = Результат + ТекущийБлок;
                ТекущийБлок = "";
            КонецЕсли;
            Результат = Результат + Символ;
            БылРазделитель = Истина;
        Иначе
            БылРазделитель = Ложь;
            ТекущийБлок = ТекущийБлок + Символ;
        КонецЕсли;
    КонецЦикла;
    
    Если Не БылРазделитель Тогда
        ДлинаБлока = СтрДлина(ТекущийБлок);
        Если ДлинаБлока < МинДлинаРаздела Тогда
            ТекущийБлок = Лев("          ", МинДлинаРаздела - ДлинаБлока) + ТекущийБлок;
        КонецЕсли;
        Результат = Результат + ТекущийБлок;
    КонецЕсли;
    
    Возврат Результат;
КонецФункции

Способ 4. Сортировка через язык запросов

Рассмотрим вариант, когда данные в таблице значений имеют одинаковый текстовый суффикс (например, 1.Кабинет, 5.Кабинет, 11.Кабинет). В этом случае упорядочивание можно выполнить с помощью объекта Запрос через вычисление длины строки:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ИсходныеДанные.Кабинет КАК Кабинет
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |    &ТаблицаДанных КАК ИсходныеДанные
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ВТ.Кабинет КАК Кабинет
    |ИЗ
    |    ВТ_Данные КАК ВТ
    |
    |УПОРЯДОЧИТЬ ПО
    |    ДЛИНА(ВТ.Кабинет),
    |    ВТ.Кабинет";

Запрос.УстановитьПараметр("ТаблицаДанных", ТЗ);
ТЗ = Запрос.Выполнить().Выгрузить();

При сортировке сначала по ДЛИНА(ВТ.Кабинет), а затем по самому значению, одноразрядные номера (длина меньше) будут расположены раньше двухразрядных, что обеспечит корректный порядок строк.

Рекомендации по проектированию структур данных

Для предотвращения подобных сложностей рекомендуется соблюдать следующие правила при проектировании структуры таблиц:

  1. Разделение данных: Храните порядковый номер (число) и текстовое наименование объекта в разных реквизитах и колонках. Склеивание строк выполняйте только на этапе непосредственного отображения информации пользователю.
  2. Форматирование при вводе: Если номер должен оставаться частью строки, задавайте формат с фиксированным количеством лидирующих нулей (например, 001.Кабинет, 002.Кабинет).
← На главную