К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как узнать номер строки в таблице значений в 1С

Как узнать номер строки в таблице значений 1С: методы Индекс(), Найти(), НайтиСтроки(), перебор коллекции. Примеры кода, ошибки и отличия индекса от номера строки.

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

Важно сразу различить два понятия. Индекс строки — это позиция в коллекции (первая строка имеет индекс 0). «Номер строки», который видит пользователь в табличном поле на форме, обычно на единицу больше индекса, потому что отображение нумеруется с единицы. Путаница между этими значениями — самая частая причина ошибок «значение индекса выходит за границы диапазона».

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

Как устроена нумерация строк в таблице значений

Таблица значений (объект ТаблицаЗначений) содержит коллекцию строк, доступных по индексу. Первый элемент всегда имеет индекс 0, последний — Количество() - 1. Обратиться к строке напрямую можно так: ТЗ[0] вернёт первую строку.

Количество строк определяется методом Количество() или через ТЗ.Количество(). Если таблица пустая, любое обращение по индексу вызовет ошибку, поэтому перед обращением к строке стоит проверять, что коллекция не пуста.

Отдельный случай — таблица значений, связанная с элементом формы (табличным полем или динамической таблицей управляемой формы). Там у строки может отображаться номер, который формируется платформой для пользователя, и он на единицу больше индекса. При программной работе всегда оперируйте индексом, а для показа пользователю прибавляйте единицу.

Метод Индекс(): получить позицию известной строки

Если у вас уже есть объект строки (значение типа СтрокаТаблицыЗначений), её позицию возвращает метод Индекс(), вызванный у таблицы:

Строка = ТЗ.Найти(ИскомыйКод, "Код");

Если Строка <> Неопределено Тогда

ИндексСтроки = ТЗ.Индекс(Строка);

НомерДляПользователя = ИндексСтроки + 1;

КонецЕсли;

Метод Индекс() сравнивает именно объект строки, а не её содержимое. Если передать строку из другой таблицы значений с идентичными данными, результат будет -1 — строка не найдена, потому что это другой объект. Это важное ограничение: метод работает только со строками той же коллекции.

Если строка не принадлежит таблице, Индекс() возвращает -1. Используйте это как проверку: значение -1 означает, что строка в данной таблице отсутствует, и обращаться к ней по индексу нельзя.

Поиск по значению: Найти() и НайтиСтроки()

Когда строка заранее неизвестна, её сначала ищут по значению в одной или нескольких колонках. Метод Найти(Значение, Колонки) возвращает первую подходящую строку или Неопределено, если совпадений нет:

НайденнаяСтрока = ТЗ.Найти(НоменклатураСсылка, "Номенклатура");

Если НайденнаяСтрока = Неопределено Тогда

// строки с таким значением нет

Иначе

Индекс = ТЗ.Индекс(НайденнаяСтрока);

КонецЕсли;

Если подходящих строк несколько и нужны все позиции, используется НайтиСтроки(ПараметрыОтбора), который возвращает массив строк. Для каждого элемента массива индекс получают тем же методом Индекс():

Отбор = Новый Структура("Склад, Признак", СкладСсылка, Истина);

МассивСтрок = ТЗ.НайтиСтроки(Отбор);

Для Каждого Стр Из МассивСтрок Цикл

Сообщить("Строка с индексом " + ТЗ.Индекс(Стр));

КонецЦикла;

Учитывайте производительность: Найти() и НайтиСтроки() перебирают коллекцию линейно. На больших таблицах (десятки тысяч строк и более) многократный поиск в цикле заметно замедляет обработку. В таких случаях лучше построить соответствие «ключ → индекс» один раз или использовать индексы таблицы значений через ТЗ.Индексы.Добавить("Колонка").

Как узнать номер строки по значению
  1. 1
    Вызовите ТЗ.Найти(Значение, "ИмяКолонки") и сохраните результат
  2. 2
    Проверьте, что результат не равен Неопределено
  3. 3
    Получите индекс: ТЗ.Индекс(НайденнаяСтрока)
  4. 4
    Для отображения пользователю прибавьте 1 к индексу
  5. 5
    При множественных совпадениях используйте НайтиСтроки() с отбором-структурой

Перебор коллекции со счётчиком

Если условие поиска сложное и не выражается простым отбором (например, сравнение по нескольким полям с вычислениями), применяют цикл. Есть два варианта: цикл Для Каждого с получением индекса через Индекс() либо классический цикл со счётчиком.

// Вариант 1: Для Каждого + Индекс()

Для Каждого Стр Из ТЗ Цикл

Если Стр.Количество > 100 И Стр.Цена < 50 Тогда

ИндексНужной = ТЗ.Индекс(Стр);

Прервать;

КонецЕсли;

КонецЦикла;

// Вариант 2: счётчик

Для Инд = 0 По ТЗ.Количество() - 1 Цикл

Если ТЗ[Инд].Количество > 100 Тогда

// Инд — и есть номер позиции

Прервать;

КонецЕсли;

КонецЦикла;

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

Для обхода в обратном порядке (например, при удалении строк) используйте убывающий счётчик от Количество() - 1 до 0. Удаление строк при прямом обходе смещает индексы оставшихся элементов и приводит к пропуску строк.

Типичные ошибки
01
Вызов ТЗ.Индекс(Строка) внутри цикла Для Каждого на большой таблице — резкое падение производительности, вместо этого используйте счётчик
02
Удаление строк при прямом переборе — индексы смещаются, часть строк пропускается; удаляйте в обратном порядке или собирайте строки в массив заранее
03
Отсутствие проверки на Неопределено после Найти() — обращение к свойствам несуществующей строки вызывает ошибку
04
Смешение индекса (с нуля) и отображаемого номера (с единицы) при выводе сообщений пользователю

Номер строки в табличном поле формы

Обычные формы

В обычных формах табличное поле связано с таблицей значений, и текущая строка доступна через ЭлементыФормы.ТабличноеПоле.ТекущиеДанные. Индекс текущей строки получают стандартно: ТЗ.Индекс(ЭлементыФормы.ТабличноеПоле.ТекущиеДанные). Отображаемый в колонке «N» номер равен этому индексу плюс один.

Управляемые формы

В управляемых формах таблица значений обычно хранится как реквизит формы типа ДанныеФормыКоллекция. У таких строк есть метод ПолучитьИдентификатор(), а обратно строку по идентификатору возвращает НайтиПоИдентификатору(). Идентификатор — это не индекс: он не меняется при сортировке, тогда как индекс зависит от текущего порядка строк.

Чтобы узнать индекс текущей строки в управляемой форме, преобразуйте данные формы в таблицу значений через РеквизитФормыВЗначение() либо используйте текущую строку элемента формы: Элементы.Таблица.ТекущаяСтрока возвращает идентификатор, по которому находится объект строки, а далее — её индекс в коллекции. Учтите, что при включённой сортировке табличного поля видимый порядок может не совпадать с порядком в исходной коллекции.

Производительность и выбор подхода

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

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

Сравнение способов

Индекс()

Когда объект строки уже известен. Не работает со строками чужой таблицы, возвращает -1

Найти()

Поиск первой строки по значению в колонках. Возвращает Неопределено при отсутствии, линейный перебор

НайтиСтроки()

Когда нужны все совпадения по отбору. Возвращает массив строк, индекс каждой берут через Индекс()

Цикл со счётчиком

Сложные условия и удаление строк. Полный контроль над индексом, но больше кода

Соответствие или запрос

Массовый поиск в цикле по большим таблицам. Требует подготовки, но даёт максимальную скорость

Как проверить результат

После получения индекса убедитесь, что он указывает на нужную строку: обратитесь к ТЗ[Индекс] и сверьте значения ключевых колонок с ожидаемыми. Для отладки удобно выводить индекс вместе со значением колонки поиска через Сообщить() или смотреть таблицу в отладчике через «Вычислить выражение».

Признаки корректной работы: индекс находится в диапазоне от 0 до Количество() - 1, значение в строке по этому индексу совпадает с искомым, а при выводе пользователю номер на единицу больше. Если индекс равен -1 или обращение по нему вызывает ошибку — строка не найдена либо взята из другой таблицы.

Контрольные точки

Контрольные точки

Значение индекса
Норма

От 0 до ТЗ.Количество() - 1

Тревожный признак

-1 или больше последнего индекса

Содержимое строки ТЗ[Индекс]
Норма

Значения колонок совпадают с искомыми

Тревожный признак

Данные другой строки — найдено не то совпадение

Поведение после сортировки
Норма

Индекс пересчитывается заново после сортировки таблицы

Тревожный признак

Используется старый индекс, сохранённый до сортировки

Итог

Что важно запомнить

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

Частые вопросы

Почему Индекс() возвращает -1, хотя строка с такими данными есть?

Метод сравнивает объект строки, а не её содержимое. Если строка получена из другой таблицы значений (например, из копии или результата Выгрузить()), это другой объект, и Индекс() вернёт -1. Ищите строку в той же таблице через Найти() по значениям колонок.

Как получить номер строки, который видит пользователь в табличном поле?

Отображаемый номер равен индексу плюс один: ТЗ.Индекс(Строка) + 1. Исключение — включённая сортировка табличного поля: тогда видимый порядок может отличаться от порядка в коллекции, и надёжнее ориентироваться на текущую строку элемента формы.

Чем Найти() отличается от НайтиСтроки()?

Найти() возвращает первую подходящую строку или Неопределено и принимает значение и список колонок. НайтиСтроки() принимает структуру отбора и возвращает массив всех подходящих строк — используйте его, когда совпадений может быть несколько.

Можно ли узнать индекс строки без перебора всей таблицы?

Если объект строки уже есть, Индекс() отработает без явного перебора в вашем коде, хотя внутри платформа всё равно ищет строку. Для массового поиска по значению постройте соответствие «значение → индекс» один раз или добавьте индекс по колонке через ТЗ.Индексы.Добавить().

Как правильно удалять строки, чтобы индексы не сбились?

Удаляйте в цикле с убывающим счётчиком от ТЗ.Количество() - 1 до 0 — тогда смещение индексов не затрагивает ещё не обработанные строки. Альтернатива: собрать удаляемые строки в массив через НайтиСтроки() и затем удалить каждую методом ТЗ.Удалить(Строка).

Что делать, если таблица значений находится в реквизите управляемой формы?

Работайте с коллекцией напрямую: у ДанныеФормыКоллекция есть метод Индекс(), а строки имеют ПолучитьИдентификатор(). Если нужны методы таблицы значений целиком, преобразуйте реквизит через РеквизитФормыВЗначение(), выполните операции и верните данные через ЗначениеВРеквизитФормы().