Каждый начинающий разработчик в среде «1С:Предприятие» рано или поздно сталкивается со странным поведением сортировки чисел. Мы ожидаем увидеть логичный ряд по возрастанию: 1, 3, 4, 23, 100, 263. Однако платформа внезапно выдает нам последовательность вида: 1, 100, 23, 263, 3, 4. Такое поведение может поставить в тупик, особенно во время непрерывной и изнурительной работы, когда внимание притупляется.
В этой статье мы подробно разберем, почему возникает такая проблема, как устроены механизмы сравнения данных в 1С, и рассмотрим наиболее эффективные, профессиональные способы сортировки без написания громоздких «велосипедов» вроде алгоритма пузырька.
Для начала давайте проанализируем ситуацию и поймем, почему платформа 1С сортирует числа столь неочевидным образом. Причина кроется в типах данных.
Если колонка вашей табличной части, таблицы значений или элементы массива имеют тип Строка, то платформа применяет к ним правила текстового (лексикографического) сравнения. При такой сортировке символы анализируются поочередно слева направо:
Для строк это абсолютно правильное поведение (именно так сортируются слова в словаре). Но для чисел, представленных в виде строк, это приводит к хаосу. Чтобы сортировка работала математически корректно, данные обязательно должны иметь тип Число.
Самый простой и методологически верный способ решить проблему раз и навсегда — хранить числовые данные только в числовом типе. Рассмотрим, как это реализовать на практике в зависимости от структуры, с которой мы работаем.
Если вы используете Табличную часть объекта (в Конфигураторе):
Откройте свойства реквизита вашей табличной части в конфигураторе и убедитесь, что в поле «Тип» указано Число с необходимой длиной и точностью, а не Строка.
Если вы динамически создаете Таблицу Значений в коде:
При добавлении колонки обязательно явно указывайте объект ОписаниеТипов. Если этого не сделать, колонка получит неопределенный тип, что может привести к нежелательному приведению к строке при заполнении.
// Создаем описание типов для числа (15 знаков всего, 2 знака после запятой)
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
КвалификаторыЧ = Новый КвалификаторыЧисла(15, 2);
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТипов, , , КвалификаторыЧ);
// Создаем таблицу значений и добавляем типизированную колонку
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("СлучайноеЧисло", ОписаниеТипаЧисло);
Если данные приходят из внешнего источника (например, текстового файла):
Обязательно используйте явное приведение типов с помощью встроенной функции Число() перед записью значения в таблицу или массив:
// Пример безопасного преобразования строкового значения в число
ЗначениеИзФайла = "263";
ЧисловоеЗначение = Число(ЗначениеИзФайла);
В исходном обсуждении автор пыталась сортировать массив вручную с помощью алгоритма «пузырька». Писать подобные алгоритмы самостоятельно в среде 1С — крайне неэффективная практика. Ручной перебор элементов на встроенном языке работает медленно, нагружает процессор и приводит к быстрому утомлению разработчика.
Если вам необходимо быстро отсортировать простой одномерный массив чисел, воспользуйтесь системным объектом СписокЗначений. Платформа выполняет сортировку этого объекта на уровне скомпилированного C++ кода, что работает в десятки раз быстрее любого самописного цикла.
Разберем этот алгоритм по шагам:
// Исходный массив с неотсортированными числами
МойМассив = Новый Массив;
МойМассив.Добавить(100);
МойМассив.Добавить(3);
МойМассив.Добавить(23);
// Переносим данные во вспомогательный СписокЗначений
Список = Новый СписокЗначений;
Список.ЗагрузитьЗначения(МойМассив);
// Сортируем встроенным методом платформы по возрастанию
Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
// Выгружаем упорядоченные значения обратно в массив
МойМассив = Список.ВыгрузитьЗначения();
Если ваши данные уже выведены на форму в табличную часть, нет никакой необходимости выгружать их в массивы для сортировки. У объекта табличной части (как на клиенте, так и на сервере) есть мощный встроенный метод Сортировать().
Рассмотрим пример синтаксиса:
// Сортировка по одной колонке по возрастанию
Объект.ИмяТабличнойЧасти.Сортировать("ИмяКолонкиЧисло Возр");
// Сортировка по нескольким колонкам (например, сначала по Группе, затем по Числу)
Объект.ИмяТабличнойЧасти.Сортировать("Группа Возр, ИмяКолонкиЧисло Убыв");
Важное ограничение: Метод Сортировать() для табличной части формы применим только к тем колонкам, которые физически существуют в метаданных как реквизиты этой табличной части. Если колонка добавлена на форму только визуально (является вычисляемым полем), отсортировать по ней напрямую не получится. В таком случае данные придется выгрузить в ТаблицуЗначений, отсортировать её и загрузить обратно на форму.
В ходе обсуждения на форуме прозвучало предложение форматировать числа перед сортировкой, дополняя их ведущими нулями (например, превращая число 1 в "000001", а 23 в "000023"). В таком виде лексикографическая сортировка строк действительно даст математически верный результат.
Давайте проанализируем, почему этот метод (хоть и кажется простым быстрым решением) таит в себе серьезные опасности:
Вердикт: Метод с ведущими нулями применим только в редких случаях для формирования специфических кодов или артикулов. Для сортировки обычных чисел использовать его категорически не рекомендуется.
Если перед вами стоит задача сортировать таблицы значений, содержащие сложные или смешанные типы данных (например, когда в одной колонке могут быть и числа, и ссылки на справочники, и строки), стандартный метод ТаблицаЗначений.Сортировать() может работать медленно. Это происходит из-за того, что система пытается получить строковое представление каждого объекта из базы данных для сравнения.
В таких сценариях на помощь приходит объект СравнениеЗначений. Он выполняет сравнение объектов по их внутреннему уникальному идентификатору (UUID), минуя долгое обращение к базе данных для формирования текстового представления.
Рассмотрим пример использования:
// Создаем объект сравнения
ОбъектСравнения = Новый СравнениеЗначений;
// Сортируем таблицу с использованием оптимизированного сравнения
МояТаблицаЗначений.Сортировать("Номенклатура, Количество", ОбъектСравнения);
Этот прием позволяет сократить время выполнения операций на больших массивах данных (от десятков тысяч строк) в несколько раз, обеспечивая отзывчивость интерфейса вашей программы.