Как в Таблице Значений 1С добавить колонку и пронумеровать строки?

Программист
← На главную

В разработке на платформе 1С часто возникает задача добавить в ТаблицуЗначений (ТЗ) служебную колонку с порядковым номером строки. Это может быть необходимо для вывода информации в определенном порядке, для сохранения исходной последовательности строк перед сортировкой или для других алгоритмических целей. На первый взгляд, задача кажется простой, но в ней есть свои нюансы, которые мы и разберем.

Основной вопрос — как программно создать новую колонку, например, с именем "Номер", и заполнить ее значениями, соответствующими индексам строк (0, 1, 2, ...) или порядковым номерам (1, 2, 3, ...).

Решение 1: Добавление колонки и заполнение в цикле

Это самый распространенный и понятный способ. Процесс состоит из двух шагов: сначала создаем структуру (колонку), а затем заполняем ее данными для каждой строки.

Шаг 1. Добавление новой колонки

Для добавления колонки в ТаблицуЗначений используется метод Колонки.Добавить(). В качестве параметров мы передаем имя новой колонки и, опционально, ее тип. Для порядкового номера лучше всего подходит тип "Число".


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

После выполнения этого кода в нашей таблице появится новая, пока еще пустая колонка "НомерСтроки".

Шаг 2. Заполнение колонки номерами

Теперь необходимо перебрать все строки таблицы и для каждой из них записать в новую колонку ее порядковый номер. Рассмотрим два варианта циклов.

Вариант А: Использование цикла Для Каждого

Этот способ является более современным и читаемым. Внутри цикла мы обращаемся к текущей строке и присваиваем ее колонке "НомерСтроки" значение, полученное через метод Индекс(). Метод Индекс() возвращает позицию строки в таблице, начиная с 0.


// Заполнение номерами, начиная с 0 (соответствует индексам)
Для Каждого СтрокаТЗ Из МояТЗ Цикл
    СтрокаТЗ.НомерСтроки = МояТЗ.Индекс(СтрокаТЗ);
КонецЦикла;

Часто пользователям удобнее видеть нумерацию, которая начинается с 1. Это легко исправить, просто добавив единицу к индексу.


// Заполнение номерами, начиная с 1
Для Каждого СтрокаТЗ Из МояТЗ Цикл
    СтрокаТЗ.НомерСтроки = МояТЗ.Индекс(СтрокаТЗ) + 1;
КонецЦикла;

Вариант Б: Использование цикла со счетчиком Для ... По ...

Классический вариант цикла, который напрямую работает с индексами. Он может быть немного быстрее на очень больших таблицах.


// Заполнение номерами, начиная с 0
Для Индекс = 0 По МояТЗ.Количество() - 1 Цикл
    СтрокаТЗ = МояТЗ[Индекс];
    СтрокаТЗ.НомерСтроки = Индекс;
КонецЦикла;

Аналогично, для нумерации с единицы просто прибавляем 1 к значению счетчика.


// Заполнение номерами, начиная с 1
Для Индекс = 0 По МояТЗ.Количество() - 1 Цикл
    СтрокаТЗ = МояТЗ[Индекс];
    СтрокаТЗ.НомерСтроки = Индекс + 1;
КонецЦикла;

Зачем нужна отдельная колонка, если есть индекс?

На форуме справедливо заметили, что у каждой строки и так есть индекс, который можно получить методом Индекс(). Так зачем же создавать дублирующую информацию? Причин может быть несколько:

  1. Сохранение исходного порядка. Представим, что вы загрузили данные, пронумеровали их, а затем выполнили сортировку таблицы по другой колонке (например, по наименованию). Визуальный порядок строк изменится, но их физические индексы останутся прежними. А вот значения в нашей колонке "НомерСтроки" сохранят первоначальный порядок. Это позволяет, например, вернуть таблицу к исходному состоянию или найти строку по ее "старому" номеру.
  2. Интерактивные действия пользователя. Если ТаблицаЗначений выведена на форму, пользователь может удалять, добавлять и перемещать строки. В этом случае индексы будут динамически меняться, и ссылаться на них станет невозможно. А значение в колонке "НомерСтроки" останется неизменным (если его не пересчитывать принудительно) и может служить "якорем".
  3. Наглядность и удобство. Для пользователя, работающего с таблицей на форме, видеть колонку с привычной нумерацией "1, 2, 3..." гораздо удобнее, чем оперировать внутренними индексами, которые начинаются с нуля и не отображаются напрямую.

Анализ типичной ошибки

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

Код выглядел примерно так:


// Неправильный код!
ТЗ2.Колонки.Добавить("НомерСтроки");
Для ЗначениеСтроки = 0 По ТЗ.Количество() - 1 Цикл
    Стр = ТЗ2.Добавить();
    Стр.НомерСтроки = ЗначениеСтроки;
КонецЦикла;

В чем здесь ошибка?

Этот код не модифицирует исходную таблицу ТЗ. Вместо этого он:

  1. Берет другую, возможно пустую, таблицу ТЗ2.
  2. Добавляет в нее колонку "НомерСтроки".
  3. В цикле добавляет в ТЗ2 новые пустые строки и заполняет только одну колонку "НомерСтроки".

В результате мы получаем не исходную таблицу с новой колонкой, а совершенно новую таблицу, содержащую только пронумерованные пустые строки. Правильный подход — работать с одной и той же таблицей, получая существующие строки по индексу и изменяя их, как было показано в решениях выше.

Дополнительные возможности и подходы

Помимо простого циклического заполнения, стоит рассмотреть и другие сценарии.

Использование уникальных идентификаторов (GUID)

Если задача состоит в том, чтобы надежно идентифицировать каждую строку, даже после удаления, добавления и перемешивания, то простой порядковый номер может не подойти. В этом случае лучше использовать УникальныйИдентификатор.


МояТЗ.Колонки.Добавить("УникальныйИдентификаторСтроки", "УникальныйИдентификатор");

Для Каждого СтрокаТЗ Из МояТЗ Цикл
    СтрокаТЗ.УникальныйИдентификаторСтроки = Новый УникальныйИдентификатор();
КонецЦикла;

Такой идентификатор будет уникален для каждой строки и никогда не изменится, что делает его идеальным ключом для связывания данных.

Нумерация для отчетов в СКД

Важный момент: если нумерация строк нужна только для отображения в печатной форме или отчете, который формируется через Систему Компоновки Данных (СКД), то нет необходимости изменять исходную ТаблицуЗначений. В СКД есть встроенные функции, которые делают это "на лету" при формировании отчета.

В вычисляемых полях макета СКД можно использовать функции НомерПоПорядку() или НомерПоПорядкуВГруппировке(). Это более правильный и производительный подход, так как он не требует лишней обработки данных и не изменяет исходный объект.

← На главную