Разработка и Конфигурация · 11 мин
Как узнать номер строки в таблице значений в 1С
Как узнать номер строки в таблице значений 1С: методы Индекс(), Найти(), НайтиСтроки(), перебор коллекции. Примеры кода, ошибки и отличия индекса от номера строки.
В таблице значений 1С строки хранятся в виде коллекции, и у каждой строки есть индекс — порядковая позиция, начинающаяся с нуля. Задача «узнать номер строки» возникает при поиске нужного значения, при синхронизации таблицы с табличным полем формы, при удалении дублей и при построении отчётов. В зависимости от ситуации применяются разные подходы: метод Индекс(), поиск через Найти() и НайтиСтроки() или прямой перебор коллекции.
Важно сразу различить два понятия. Индекс строки — это позиция в коллекции (первая строка имеет индекс 0). «Номер строки», который видит пользователь в табличном поле на форме, обычно на единицу больше индекса, потому что отображение нумеруется с единицы. Путаница между этими значениями — самая частая причина ошибок «значение индекса выходит за границы диапазона».
Ниже разобраны рабочие способы получить индекс и номер строки: для известной строки, по значению в колонке, при переборе, а также особенности работы с табличными полями управляемых и обычных форм.
Как устроена нумерация строк в таблице значений
Таблица значений (объект ТаблицаЗначений) содержит коллекцию строк, доступных по индексу. Первый элемент всегда имеет индекс 0, последний — Количество() - 1. Обратиться к строке напрямую можно так: ТЗ[0] вернёт первую строку.
Количество строк определяется методом Количество() или через ТЗ.Количество(). Если таблица пустая, любое обращение по индексу вызовет ошибку, поэтому перед обращением к строке стоит проверять, что коллекция не пуста.
Отдельный случай — таблица значений, связанная с элементом формы (табличным полем или динамической таблицей управляемой формы). Там у строки может отображаться номер, который формируется платформой для пользователя, и он на единицу больше индекса. При программной работе всегда оперируйте индексом, а для показа пользователю прибавляйте единицу.
Метод Индекс(): получить позицию известной строки
Если у вас уже есть объект строки (значение типа СтрокаТаблицыЗначений), её позицию возвращает метод Индекс(), вызванный у таблицы:
Строка = ТЗ.Найти(ИскомыйКод, "Код");
Если Строка <> Неопределено Тогда
ИндексСтроки = ТЗ.Индекс(Строка);
НомерДляПользователя = ИндексСтроки + 1;
КонецЕсли;
Метод Индекс() сравнивает именно объект строки, а не её содержимое. Если передать строку из другой таблицы значений с идентичными данными, результат будет -1 — строка не найдена, потому что это другой объект. Это важное ограничение: метод работает только со строками той же коллекции.
Если строка не принадлежит таблице, Индекс() возвращает -1. Используйте это как проверку: значение -1 означает, что строка в данной таблице отсутствует, и обращаться к ней по индексу нельзя.
Поиск по значению: Найти() и НайтиСтроки()
Когда строка заранее неизвестна, её сначала ищут по значению в одной или нескольких колонках. Метод Найти(Значение, Колонки) возвращает первую подходящую строку или Неопределено, если совпадений нет:
НайденнаяСтрока = ТЗ.Найти(НоменклатураСсылка, "Номенклатура");
Если НайденнаяСтрока = Неопределено Тогда
// строки с таким значением нет
Иначе
Индекс = ТЗ.Индекс(НайденнаяСтрока);
КонецЕсли;
Если подходящих строк несколько и нужны все позиции, используется НайтиСтроки(ПараметрыОтбора), который возвращает массив строк. Для каждого элемента массива индекс получают тем же методом Индекс():
Отбор = Новый Структура("Склад, Признак", СкладСсылка, Истина);
МассивСтрок = ТЗ.НайтиСтроки(Отбор);
Для Каждого Стр Из МассивСтрок Цикл
Сообщить("Строка с индексом " + ТЗ.Индекс(Стр));
КонецЦикла;
Учитывайте производительность: Найти() и НайтиСтроки() перебирают коллекцию линейно. На больших таблицах (десятки тысяч строк и более) многократный поиск в цикле заметно замедляет обработку. В таких случаях лучше построить соответствие «ключ → индекс» один раз или использовать индексы таблицы значений через ТЗ.Индексы.Добавить("Колонка").
- 1Вызовите ТЗ.Найти(Значение, "ИмяКолонки") и сохраните результат
- 2Проверьте, что результат не равен Неопределено
- 3Получите индекс: ТЗ.Индекс(НайденнаяСтрока)
- 4Для отображения пользователю прибавьте 1 к индексу
- 5При множественных совпадениях используйте НайтиСтроки() с отбором-структурой
Перебор коллекции со счётчиком
Если условие поиска сложное и не выражается простым отбором (например, сравнение по нескольким полям с вычислениями), применяют цикл. Есть два варианта: цикл Для Каждого с получением индекса через Индекс() либо классический цикл со счётчиком.
// Вариант 1: Для Каждого + Индекс()
Для Каждого Стр Из ТЗ Цикл
Если Стр.Количество > 100 И Стр.Цена < 50 Тогда
ИндексНужной = ТЗ.Индекс(Стр);
Прервать;
КонецЕсли;
КонецЦикла;
// Вариант 2: счётчик
Для Инд = 0 По ТЗ.Количество() - 1 Цикл
Если ТЗ[Инд].Количество > 100 Тогда
// Инд — и есть номер позиции
Прервать;
КонецЕсли;
КонецЦикла;
Вариант со счётчиком предпочтительнее, когда индекс нужен в каждой итерации: вызов Индекс() внутри цикла выполняет поиск строки в коллекции, что на больших таблицах превращает линейный обход в квадратичный по времени. Это одна из скрытых причин «тормозов» в обработках.
Для обхода в обратном порядке (например, при удалении строк) используйте убывающий счётчик от Количество() - 1 до 0. Удаление строк при прямом обходе смещает индексы оставшихся элементов и приводит к пропуску строк.
Номер строки в табличном поле формы
Обычные формы
В обычных формах табличное поле связано с таблицей значений, и текущая строка доступна через ЭлементыФормы.ТабличноеПоле.ТекущиеДанные. Индекс текущей строки получают стандартно: ТЗ.Индекс(ЭлементыФормы.ТабличноеПоле.ТекущиеДанные). Отображаемый в колонке «N» номер равен этому индексу плюс один.
Управляемые формы
В управляемых формах таблица значений обычно хранится как реквизит формы типа ДанныеФормыКоллекция. У таких строк есть метод ПолучитьИдентификатор(), а обратно строку по идентификатору возвращает НайтиПоИдентификатору(). Идентификатор — это не индекс: он не меняется при сортировке, тогда как индекс зависит от текущего порядка строк.
Чтобы узнать индекс текущей строки в управляемой форме, преобразуйте данные формы в таблицу значений через РеквизитФормыВЗначение() либо используйте текущую строку элемента формы: Элементы.Таблица.ТекущаяСтрока возвращает идентификатор, по которому находится объект строки, а далее — её индекс в коллекции. Учтите, что при включённой сортировке табличного поля видимый порядок может не совпадать с порядком в исходной коллекции.
Производительность и выбор подхода
Для разового поиска в таблице до нескольких тысяч строк разницы между методами практически нет — выбирайте самый читаемый вариант. Проблемы начинаются, когда поиск выполняется внутри цикла по другой коллекции: например, для каждой строки документа ищется соответствие в большой таблице.
Способы ускорения: добавить индекс по колонке поиска (ТЗ.Индексы.Добавить("Номенклатура")), построить Соответствие с ключом из значения колонки и значением-индексом, либо перенести соединение данных в запрос, где платформа выполнит его оптимальнее. Выбор зависит от того, сколько раз выполняется поиск и насколько велика таблица.
Сравнение способов
Когда объект строки уже известен. Не работает со строками чужой таблицы, возвращает -1
Поиск первой строки по значению в колонках. Возвращает Неопределено при отсутствии, линейный перебор
Когда нужны все совпадения по отбору. Возвращает массив строк, индекс каждой берут через Индекс()
Сложные условия и удаление строк. Полный контроль над индексом, но больше кода
Массовый поиск в цикле по большим таблицам. Требует подготовки, но даёт максимальную скорость
Как проверить результат
После получения индекса убедитесь, что он указывает на нужную строку: обратитесь к ТЗ[Индекс] и сверьте значения ключевых колонок с ожидаемыми. Для отладки удобно выводить индекс вместе со значением колонки поиска через Сообщить() или смотреть таблицу в отладчике через «Вычислить выражение».
Признаки корректной работы: индекс находится в диапазоне от 0 до Количество() - 1, значение в строке по этому индексу совпадает с искомым, а при выводе пользователю номер на единицу больше. Если индекс равен -1 или обращение по нему вызывает ошибку — строка не найдена либо взята из другой таблицы.
Контрольные точки
От 0 до ТЗ.Количество() - 1
-1 или больше последнего индекса
Значения колонок совпадают с искомыми
Данные другой строки — найдено не то совпадение
Индекс пересчитывается заново после сортировки таблицы
Используется старый индекс, сохранённый до сортировки
Что важно запомнить
Индекс строки получают методом Индекс(), нумерация идёт с нуля, отображаемый номер — индекс плюс один. Неизвестную строку сначала ищут через Найти() или НайтиСтроки() с проверкой на Неопределено. В больших таблицах избегайте Индекс() и Найти() внутри циклов — используйте счётчик, индексы колонок или соответствия.
Частые вопросы
Почему Индекс() возвращает -1, хотя строка с такими данными есть?
Метод сравнивает объект строки, а не её содержимое. Если строка получена из другой таблицы значений (например, из копии или результата Выгрузить()), это другой объект, и Индекс() вернёт -1. Ищите строку в той же таблице через Найти() по значениям колонок.
Как получить номер строки, который видит пользователь в табличном поле?
Отображаемый номер равен индексу плюс один: ТЗ.Индекс(Строка) + 1. Исключение — включённая сортировка табличного поля: тогда видимый порядок может отличаться от порядка в коллекции, и надёжнее ориентироваться на текущую строку элемента формы.
Чем Найти() отличается от НайтиСтроки()?
Найти() возвращает первую подходящую строку или Неопределено и принимает значение и список колонок. НайтиСтроки() принимает структуру отбора и возвращает массив всех подходящих строк — используйте его, когда совпадений может быть несколько.
Можно ли узнать индекс строки без перебора всей таблицы?
Если объект строки уже есть, Индекс() отработает без явного перебора в вашем коде, хотя внутри платформа всё равно ищет строку. Для массового поиска по значению постройте соответствие «значение → индекс» один раз или добавьте индекс по колонке через ТЗ.Индексы.Добавить().
Как правильно удалять строки, чтобы индексы не сбились?
Удаляйте в цикле с убывающим счётчиком от ТЗ.Количество() - 1 до 0 — тогда смещение индексов не затрагивает ещё не обработанные строки. Альтернатива: собрать удаляемые строки в массив через НайтиСтроки() и затем удалить каждую методом ТЗ.Удалить(Строка).
Что делать, если таблица значений находится в реквизите управляемой формы?
Работайте с коллекцией напрямую: у ДанныеФормыКоллекция есть метод Индекс(), а строки имеют ПолучитьИдентификатор(). Если нужны методы таблицы значений целиком, преобразуйте реквизит через РеквизитФормыВЗначение(), выполните операции и верните данные через ЗначениеВРеквизитФормы().