Натуральная сортировка строк с числовыми данными в 1С
Разработка - Инструментарий разработчика
Файлы
ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.
Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний
Оформите подписку на компанию для решения рабочих задач
Оформить подписку и скачать решение со скидкойВы можете заказать платную доработку или адаптацию этой разработки под вашу конфигурацию на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.
Натуральная сортировка строк в 1С: универсальное решение
Проблема
Стандартная сортировка строк в 1С — лексикографическая. Это значит, что "10" < "2", "А10" < "А2", и любой список кодификаторов, артикулов, номеров документов выглядит «неправильно»:
1, 10, 100, 11, 2, 20, 3, А10, А2, Б1, Б10, Б2
Хочется видеть естественный порядок:
1, 2, 3, 10, 11, 20, 100, А2, А10, Б1, Б2, Б10
Проблема усугубляется тем, что реальные данные редко бывают «чистыми числами». В наименованиях встречаются:
- числа с ведущими нулями:
001,А016,001 00 01; - буквы, склеенные с числами:
А15,1А,А0017Б; - диапазоны:
1-12,24-25, 28-30; - иерархия с разделителями:
13.1,13.10.1; - смешанные конструкции:
АА15.45Б.В,003К-0038/1.
В 1С нет возможности задать кастомную сортировку для реквизита. Поэтому «чистый» запрос, который бы всё это сортировал, построить нельзя — либо он будет громоздким, либо развалится на первой же нестандартной строке из-за ограничений. Не лучше дела обстоят с сортировкой различных коллекций.
Решение: предвычисленный ключ сортировки
Идея. Один раз преобразовать строку в другую строку-ключ так, чтобы обычная лексикографическая сортировка по ключу дала натуральный порядок. Ключ хранится в индексированном реквизите, и в запросах / СКД достаточно написать УПОРЯДОЧИТЬ ПО КлючСортировки.
Преимущества:
- Одна колонка — работает и в запросах, и в СКД, и в динамических списках, и в консоли.
- Максимальная скорость — сортировка выполняется СУБД по индексу.
- Один универсальный алгоритм — не нужно выбирать «режим» под каждый тип кода, все данные сортируются одинаково.
- Легко поддерживать — вся логика в одной функции.
Алгоритм
Строка разбивается на группы подряд идущих цифр и группы подряд идущих не-цифр. Каждая группа кодируется:
| Тип группы | Кодировка |
|---|---|
| Цифры | "0" + ширина (3 знака) + значение без ведущих нулей |
| Не-цифры | "1" + текст как есть |
Три свойства ключа дают натуральный порядок:
- Маркер
"0"(код 48) меньше маркера"1"(код 49). Значит на одной позиции цифровая группа всегда идёт раньше текстовой. Это даёт1 < 1А,А15 < АА15,2.АА.14 < АА15.45Б.В. - Ширина числа кодируется тремя знаками с ведущими нулями.
001,002, …,010,100,999. Сначала сравниваются длины — однозначные числа раньше двузначных, те раньше трёхзначных. При равной длине сравнение идёт по самим цифрам, то есть числа сравниваются как числа:9 < 10 < 100. - Ведущие нули схлопываются —
001 = 1,А016 = А16.
Примеры кодирования
| Входная строка | Группы | Ключ (фрагмент) |
|---|---|---|
1 |
1 |
00011 |
1А |
1, А |
00011 1А |
10 |
10 |
00210 |
А15 |
А, 15 |
1А 00215 |
А016 |
А, 016→16 |
1А 00216 |
А0017Б |
А, 0017→17, Б |
1А 00217 1Б |
001 00 01 |
001→1, , 00→0, , 01→1 |
00011 1 00010 1 00011 |
003К-0038/1 |
003→3, К, , 0038→38, , 1 |
00013 1К 000238 1 00011 |
1-12 |
1, -, 12 |
00011 1- 00212 |
13.10 |
13, ., 10 |
00213 1. 00210 |
13.10.1 |
13, ., 10, ., 1 |
00213 1. 00210 1. 0011 |
Код модуля
Модуль оформлен в стиле БСП: публичные методы в области ПрограммныйИнтерфейс, у каждой функции шапка с описанием, параметрами и примером. Для внедрения создаете общий серверный модуль НатуральнаяСортировкаи скопируйте в него код из модуля объекта обработки.
Подключение
Шаг 1. Реквизит справочника
Добавьте в справочник (или документ) реквизит:
- Имя:
КлючСортировки<ИмяРеквизитаДляСортировки> - Тип:
Строка, длина 150-200 - Индексирование:
Индексировать
Если у объекта несколько реквизитов для сортировки (например, Артикул и Код), добавляйте по реквизиту на каждый: КлючСортировкиАртикул, КлючСортировкиКод.
Шаг 2. Заполнение при записи
В модуле объекта укажите реквизиты для которых нужно создать ключи:
Процедура ПередЗаписью(Отказ)
НатуральнаяСортировка.ЗаполнитьКлючиСортировки(ЭтотОбъект, "Артикул");
КонецПроцедуры
Шаг 3. Разовое заполнение существующих данных
Выборка = Справочники.Подразделения.Выбрать();
Пока Выборка.Следующий() Цикл
Попытка
Выборка.Ссылка.ПолучитьОбъект().Записать();
Исключение
КонецПопытки;
КонецЦикла;
Шаг 4. Использование в запросе
ВЫБРАТЬ
Ссылка, ИдПодразделения
ИЗ
Справочник.Подразделения
ГДЕ Проект = &Проект
УПОРЯДОЧИТЬ ПО КлючСортировкиИдПодразделения
Шаг 5. Использование в СКД
- Добавьте
КлючСортировкив набор данных. - На вкладке «Настройки» задайте сортировку по этому полю.
- Снимите галку «Видимость» у колонки — пользователь её не увидит.
Проверка на смешанных данных
С помощью обработки вы можете проверить как алгоритм справляется с вашими кодами без необходимости внедрения. Просто вставьте коды в окно обработки, нажмите кнопку Натуральная сортировка и сравните классическую и натуральную сортировки.
Известные ограничения
1. Пересекающиеся диапазоны
Функция сортирует диапазоны по первому числу. Для типичных данных (1-12, 13, 14-18) это совпадает с сортировкой по верхней границе. Но для убывающих/пересекающихся диапазонов (5-100 и 20-25) первый и последний номера дадут разный порядок. Если такие данные возможны, в ключ можно добавить блок с последним числом строки — но это редкая ситуация, и в большинстве задач не требуется.
2. Схлопывание ведущих нулей универсально
001 = 1, А001 = А1. Это следует из требования «ведущие нули незначащи». Если в ваших данных 00ЗК (с кириллической З) и 003К (с цифрой 3) — разные вещи, схлопывание не поможет: кириллица и цифра — разные символы, алгоритм разберёт их по-разному.
3. Кириллица vs латиница
Визуально неотличимые З/3, О/0, А/A, В/B, С/C, Р/P, Е/E, Т/T, Н/H, У/Y, К/K, Х/X, М/M при импорте из Excel, почты, PDF могут привести к тому, что две визуально одинаковые строки дадут разные ключи. Если данные приходят из внешних источников — санитизируйте вход перед вызовом функции, но осторожно, чтобы не сломать слова.
4. Нормализация разделителей
Параметр НормализоватьРазделители = Истина заменяет ., ,, -, /, ;, :, _, \, |, скобки, пробелы и другие разделители на единый пробел. Из-за этого 1.5 и 1-5 дадут одинаковый ключ. Если различать их важно — передайте Ложь.
5. Регистр
Функция приводит строку к верхнему регистру. Если нужна регистрозависимая сортировка — уберите ВРег, но помните, что тогда А15 и а15 разойдутся.
6. Ограничение на длину числа
Ширина префикса длины — 3 знака, значит одно число может содержать до 999 цифр. Для практических данных — с огромным запасом.
7. Стабильность
Ключ заканчивается пробелом и исходной строкой в верхнем регистре. Если два разных значения дают одинаковый ключ (А016 и А16), порядок между ними будет определяться исходным написанием — то есть воспроизводимо и предсказуемо.
Почему не чистый запрос
Периодически возникает вопрос: «а можно то же самое написать прямо в SQL-запросе?». Ответ: нет, нельзя без потери читаемости и надёжности.
Сортировка коллекций
Так же в модуле есть функция, отвечающая за сортировку коллекций, она описана в основном модуле. Ниже примеры использования для каждого поддерживаемого типа коллекции.
Примеры использования
1. Таблица значений — простая сортировка по одной колонке
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
// ... заполнение ...
НатуральнаяСортировка.Сортировать(ТЗ, "!Артикул");
2. Таблица значений — сортировка по нескольким колонкам с разными направлениями
// Сначала по группе (обычная сортировка), затем внутри группы
// по артикулу — натурально, по убыванию
НатуральнаяСортировка.Сортировать(ТЗ, "Группа, !Артикул Убыв");
3. Сохранить служебные колонки после сортировки
Иногда полезно оставить колонки с ключами — например, для отладки или для повторного использования.
НатуральнаяСортировка.СортироватьТЗ(ТЗ, "!Артикул", Ложь);
// В ТЗ появится колонка "КлючСортировкиАртикул" с вычисленными ключами
4. Табличная часть объекта
// Товары — табличная часть справочника или документа
Об = Ссылка.ПолучитьОбъект();
НатуральнаяСортировка.Сортировать(Об.Товары, "!Артикул");
5. Табличная часть формы (ДанныеФормыКоллекция)
// Прямо на форме
НатуральнаяСортировка.Сортировать(Объект.Товары, "!Артикул");
6. Массив строк
Массив = Новый Массив;
Массив.Добавить("А15");
Массив.Добавить("А2");
Массив.Добавить("А10");
Массив.Добавить("1А");
Массив.Добавить("10");
Массив.Добавить("2");
НатуральнаяСортировка.Сортировать(Массив);
// Результат: "2", "10", "1А", "А2", "А10", "А15"
Обратите внимание: массив сортируется по строковому представлению элементов, по сути может содержать любые типы данных.
7 Список значений
Список = Новый СписокЗначений;
Список.Добавить("А15", "Позиция 15");
Список.Добавить("А2", "Позиция 2");
Список.Добавить("А10", "Позиция 10");
НатуральнаяСортировка.Сортировать(Список);
// Порядок: "А2", "А10", "А15"
Так же можно сортировать по представлению или по значению и представлению. Информация об отметке и картинки не теряются.
Итог
- Один универсальный алгоритм —
ПолучитьКлючСортировки(Строка)— покрывает числа, буквы, диапазоны, иерархию и любые их сочетания. - Один индексированный реквизит —
КлючСортировки— и одна строкаУПОРЯДОЧИТЬ ПОво всех запросах, СКД и динамических списках. - Одна точка изменения —
ПередЗаписьюобъекта и разовая обработка для существующих данных. - Прозрачное поведение — ведущие нули незначащи, короткое значение раньше длинного, цифры раньше букв, регистр не важен.
Проверено на следующих конфигурациях и релизах:
- Управление торговлей, редакция 11, релизы 11.6.1.61
Вступайте в нашу телеграмм-группу Инфостарт