В процессе разработки в среде 1С:Предприятие 8.3 часто возникает необходимость отсортировать табличную часть документа или обработки по определенному реквизиту (например, КлючСвязи), сохранив при этом исходный порядок следования строк внутри групп. На первый взгляд, решение кажется очевидным: вызвать метод Сортировать("КлючСвязи, НомерСтроки"). Однако на практике программисты сталкиваются с тем, что строки меняются местами непредсказуемым образом, а системное поле НомерСтроки игнорируется или ведет себя некорректно.
В этой статье мы подробно проанализируем, почему стандартный метод сортировки не подходит для работы с системными полями, и разберем несколько надежных способов реализации «правильной» сортировки.
Разберем ситуацию, с которой столкнулся автор темы. При попытке выполнить код ТЧ.Сортировать("КлючСвязи, НомерСтроки") платформа не выдает ошибку (в новых версиях), но и не выполняет ожидаемую сортировку. Этому есть несколько технических объяснений:
НомерСтроки является системным и вычисляемым. Оно жестко привязано к текущему индексу строки в коллекции. Как только алгоритм сортировки перемещает строку, её НомерСтроки тут же меняется. Попытка сортировать по значению, которое меняется прямо в процессе сортировки, приводит к логической ошибке.ТабличнаяЧасть имеет встроенный метод Сортировать(), который официально предназначен для работы с измерениями (в случае регистров) или реквизитами. Системные поля, такие как НомерСтроки, не входят в список доступных колонок для этого метода, даже если платформа «молча» пропускает такой параметр.КлючСвязи) совпадают, относительный порядок строк не гарантируется. Без указания дополнительного статического уникального поля строки будут перемешаны.Рассмотрим самый надежный и производительный способ, который рекомендуют опытные разработчики. Поскольку мы не можем использовать НомерСтроки напрямую, нам нужно зафиксировать текущий порядок строк в дополнительной колонке. Реализуем это через выгрузку в объект ТаблицаЗначений.
Проанализируем шаги выполнения этой задачи:
ИсходныйПорядок) с типом Число.Посмотрим на пример кода, реализующего этот алгоритм:
// Выгружаем ТЧ в таблицу значений
ТЗ = Объект.Товары.Выгрузить();
// Добавляем колонку для фиксации порядка
ТЗ.Колонки.Добавить("ИсходныйПорядок", Новый ОписаниеТипов("Число"));
// Заполняем колонку
Для Каждого СтрокаТЗ Из ТЗ Цикл
СтрокаТЗ.ИсходныйПорядок = ТЗ.Индекс(СтрокаТЗ);
КонецЦикла;
// Сортируем по ключу и нашему сохраненному порядку
ТЗ.Сортировать("КлючСвязи Возр, ИсходныйПорядок Возр");
// Загружаем данные обратно в ТЧ
Объект.Товары.Загрузить(ТЗ);
Важный момент: Метод Загрузить() полностью очищает текущую табличную часть и заполняет её строками из таблицы значений, строго соблюдая их порядок. Это штатное поведение платформы, которое гарантирует нужный нам результат.
Если по каким-то причинам вы не хотите использовать метод Загрузить() (например, чтобы сохранить ссылки на строки табличной части в памяти или избежать полной перезагрузки), можно воспользоваться механизмом запросов. В отличие от метода Сортировать(), механизм запросов позволяет обращаться к полю НомерСтроки как к статичному значению.
Разберем пример кода, предложенный автором темы, где используется запрос к табличной части и последующее перемещение строк методом Сдвинуть():
Процедура СортировкаТЧПоПолюВПорядкеСледования(ТЧ, ИмяПоляСортировки)
Запрос = Новый Запрос;
// Передаем табличную часть как параметр запроса
Запрос.УстановитьПараметр("ТЧ", ТЧ);
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ.НомерСтроки КАК НомерСтроки,
| ТЧ." + ИмяПоляСортировки + " КАК ПолеСортировки
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ТЧ КАК ТЧ
|;
|ВЫБРАТЬ
| ВТ_Данные.НомерСтроки
|ИЗ
| ВТ_Данные КАК ВТ_Данные
|УПОРЯДОЧИТЬ ПО
| ПолеСортировки,
| НомерСтроки";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
ТекИндекс = 0;
Пока Выборка.Следующий() Цикл
// Находим строку по её старому номеру (индексу)
// Внимание: этот метод требует осторожности при больших объемах данных
СтрокаТЧ = ТЧ.Получить(Выборка.НомерСтроки - 1);
Сдвиг = ТекИндекс - ТЧ.Индекс(СтрокаТЧ);
Если Сдвиг <> 0 Тогда
ТЧ.Сдвинуть(СтрокаТЧ, Сдвиг);
КонецЕсли;
ТекИндекс = ТекИндекс + 1;
КонецЦикла;
КонецПроцедуры
Проанализируем этот подход. Использование Сдвинуть() в цикле является более трудоемкой операцией для системы, чем Загрузить(), так как при каждом сдвиге платформа пересчитывает индексы. Однако этот метод позволяет оставить объекты строк в памяти неизменными, что может быть критично для сложной логики форм.
Мы выяснили, что стандартная сортировка табличной части по НомерСтроки невозможна из-за ограничений платформы 1С:Предприятие 8. Для реализации устойчивой сортировки (stable sort) следует использовать один из двух подходов:
ТаблицаЗначений, добавить вспомогательную числовую колонку, заполнить её индексами, отсортировать и загрузить обратно. Это самый быстрый и "чистый" способ.Помните, что поле НомерСтроки в запросе — это число, а в методе Сортировать() — динамический указатель, который нельзя использовать как ключ сортировки.