Как правильно отсортировать строковые номера квартир и домов с буквами в 1С?

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

При работе со строковыми данными в платформах 1С программисты регулярно сталкиваются с проблемой алфавитно-цифровой сортировки. Типовой метод сортировки ТаблицаЗначений.Сортировать() для текстового поля организует строки по символьным кодам. В результате номера квартир или домов выстраиваются в некорректном с точки зрения человека порядке: 1, 11, 12... 19, 2, 20, 21.

Прямое приведение значения к типу Число невозможно, так как в реальных базах данных часто встречаются адреса с литерами, дробями и суффиксами (например, 19Б, 12/1 или 15 пом. 2). В этой статье мы подробно разберем несколько эффективных алгоритмов естественной сортировки (Natural Sort Order) в 1С.

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

Проанализируем ситуацию на базовом уровне. Строковый тип данных сравнивает символы поочередно слева направо. Символ "1" имеет меньший код, чем символ "2". Поэтому строка "180" при стандартном сравнении считается "меньше", чем строка "2", так как первый символ первого числа меньше первого символа второго числа.

Чтобы организовать правильный (натуральный) порядок отображения, необходимо разделить значение на основной числовой блок и буквенно-символьный суффикс либо нормализовать ширину числовых блоков.

Способ 1. Разделение номера на число и букву в две вспомогательные колонки

Наиболее популярный и наглядный подход — добавление в таблицу значений двух служебных колонок: для основной числовой части и для литеры (буквенного суффикса).

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

  1. Добавляем в объект ТаблицаЗначений две временные колонки: ЧисловаяЧасть (тип Число) и БуквеннаяЧасть (тип Строка).
  2. Обходим таблицу в цикле, выделяя из строки начальные цифры.
  3. Заполняем созданные колонки и выполняем сортировку по двум полям подряд.

Посмотрим на пример кода на встроенном языке 1С:


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

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

// Выполняем сортировку сначала по числу, затем по букве
ТаблицаКвартир.Сортировать("ЧисловаяЧасть Возр, БуквеннаяЧасть Возр");

После вызова метода Сортировать() данные расположатся в правильном порядке: 2, 19, 19А, 19Б, 180.

Способ 2. Формирование единого индексного ключа с лидирующими нулями

Если нет желания плодить несколько колонок, можно обойтись добавлением одного строкового поля-ключа. Суть метода заключается в приведении числовой части строки к фиксированной длине путем дополнения лидирующими нулями.

Разберем пример:

  1. Строка "2" превращается в "0000000002".
  2. Строка "19Б" превращается в "0000000019Б".
  3. Строка "180" превращается в "0000000180".

При стандартной строковой сортировке строка "0000000002" встанет раньше "0000000019Б", а "0000000019Б" окажется раньше "0000000180", что полностью решает задачу.

Рассмотрим функцию формирования такого ключа:


Функция ПолучитьКлючСортировки(Знач НомерКвартиры)
    НомерСтр = СокрЛП(НомерКвартиры);
    Индекс = 1;
    ДлинаСтр = СтрДлина(НомерСтр);
    
    Пока Индекс <= ДлинаСтр Цикл
        КодСимв = КодСимвола(НомерСтр, Индекс);
        Если Не (КодСимв >= 48 И КодСимв <= 57) Тогда
            Прервать;
        КонецЕсли;
        Индекс = Индекс + 1;
    КонецЦикла;
    
    ЧисловаяЧасть = Лев(НомерСтр, Индекс - 1);
    БуквеннаяЧасть = Сред(НомерСтр, Индекс);
    
    Если ЗначениеЗаполнено(ЧисловаяЧасть) Тогда
        // Форматируем число до 10 знаков с лидирующими нулями
        ЧислоВыровненное = Формат(Число(ЧисловаяЧасть), "ЧЦ=10; ЧДЦ=0; ЧЧ=0; ЧГ=0");
        Возврат ЧислоВыровненное + БуквеннаяЧасть;
    Иначе
        Возврат НомерСтр;
    КонецЕсли;
КонецФункции

Способ 3. Использование регулярных выражений (RegExp) для сложных номеров

Когда номера квартир содержат сложные комбинации с корпусами, строениями или дробями (например, 19-Б, 12/3, 15 пом. 2), обычного поиска символов может оказаться недостаточно. В таких случаях удобно задействовать объект COMObject("VBScript.RegExp").

Проанализируем применение регулярных выражений:


РегВыр = Новый COMОбъект("VBScript.RegExp");
РегВыр.IgnoreCase = Истина;
РегВыр.Global = Ложь;
// Шаблон находит начальную группу цифр и остаток строки
РегВыр.Pattern = "^(\d+)(.*)$";

Для Каждого Стр Из ТаблицаЗначений Цикл
    Совпадения = РегВыр.Execute(СокрЛП(Стр.НомерКвартиры));
    Если Совпадения.Количество() > 0 Тогда
        Группы = Совпадения.Item(0).SubMatches;
        ЧислоЧасть = Число(Группы.Item(0));
        БукваЧасть = Группы.Item(1);
        
        Стр.ИндексСортировки = Формат(ЧислоЧасть, "ЧЦ=10; ЧДЦ=0; ЧЧ=0; ЧГ=0") + БукваЧасть;
    Иначе
        Стр.ИндексСортировки = Стр.НомерКвартиры;
    КонецЕсли;
КонецЦикла;

ТаблицаЗначений.Сортировать("ИндексСортировки Возр");

Способ 4. Натуральная сортировка в отчетах СКД и Запросах

Если задача возникает при построении отчета в Системе компоновки данных (СКД), изменять таблицу значений в коде не требуется. Для вывода данных в естественном порядке применяют **Вычисляемые поля** СКД.

Разберем порядок действий в компоновщике макета:

  1. На вкладке Вычисляемые поля создаем новое поле ИндексСортировкиКвартиры.
  2. В качестве выражения вычисляемого поля указываем вызов функции из общего модуля: МойОбщийМодуль.ПолучитьКлючСортировки(НомерКвартиры).
  3. На вкладке Настройки в секции Сортировка добавляем поле ИндексСортировкиКвартиры по возрастанию.

Способ 5. Архитектурный подход к хранению данных

В специализированных конфигурациях (например, в решениях для ЖКХ и управления недвижимостью) проблему решают на этапе проектирования структуры базы данных. Вместо единого текстового реквизита создают три независимых поля:

Пользовательское представление (например, "19Б/1") сшивается программно при выводе на форму с помощью метода СформироватьПредставление(). Такой подход гарантирует мгновенную сортировку средств СУБД с использованием стандартных индексов таблицы без дополнительных вычислений в runtime.

← На главную