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

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

Как в 1С получить индекс строки: таблица значений, табличная часть, форма

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

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

Главная сложность в том, что индексация коллекций в 1С начинается с нуля, а нумерация строк, которую видит пользователь в табличной части (колонка «№»), — с единицы. Это различие регулярно становится источником ошибок, особенно когда разработчик смешивает программный индекс и номер строки документа.

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

Как устроена индексация строк в 1С

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

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

Отдельно существует свойство НомерСтроки у строк табличных частей документов и справочников. Это видимый пользователю номер, который начинается с единицы и может автоматически перенумеровываться при удалении строк. Связь проста: НомерСтроки = Индекс + 1, но полагаться на неё стоит только пока нумерация не нарушена вручную.

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

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

ТЗ = Новый ТаблицаЗначений;

ТЗ.Колонки.Добавить("Номенклатура");

// ... заполнение таблицы

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

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

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

Сообщить("Строка найдена, индекс: " + ИндексСтроки);

Иначе

Сообщить("Строка не найдена");

КонецЕсли;

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

Альтернатива без метода Индекс() — получить позицию в цикле, когда нужно отслеживать индекс каждой строки при обходе:

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

СтрокаТЗ = ТЗ[Индекс];

// работа со строкой, Индекс доступен

КонецЦикла;

Поиск индекса по нескольким условиям

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

Отбор = Новый Структура("Склад, Номенклатура", Склад, Товар);

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

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

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

КонецЦикла;

Индекс строки табличной части документа

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

СтрокаТЧ = ДокументОбъект.Товары.Найти(НужныйТовар, "Номенклатура");

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

ИндексСтроки = ДокументОбъект.Товары.Индекс(СтрокаТЧ);

// вставить строку сразу после найденной

НоваяСтрока = ДокументОбъект.Товары.Вставить(ИндексСтроки + 1);

КонецЕсли;

Для табличной части дополнительно доступно свойство НомерСтроки: СтрокаТЧ.НомерСтроки вернёт видимый номер. Если требуется именно номер для сообщения пользователю («ошибка в строке 5»), используйте его, а не индекс — пользователю нулевая нумерация будет непонятна.

Индекс текущей строки на форме

На управляемой форме табличная часть и таблица значений отображаются через элемент «ТаблицаФормы», а данные хранятся в реквизите типа «ДанныеФормыКоллекция». Здесь прямого метода Индекс() нет, и подход отличается.

Текущую строку таблицы на форме получают через Элементы.Товары.ТекущиеДанные — это объект «ДанныеФормыЭлементКоллекции». Чтобы узнать её позицию, используют идентификатор строки и метод Индекс() коллекции данных формы либо вычисляют через НомерСтроки:

ТекущиеДанные = Элементы.Товары.ТекущиеДанные;

Если ТекущиеДанные <> Неопределено Тогда

// вариант 1: через номер строки табличной части

ИндексСтроки = ТекущиеДанные.НомерСтроки - 1;

// вариант 2: через идентификатор строки

Идентификатор = Элементы.Товары.ТекущаяСтрока;

СтрокаКоллекции = Объект.Товары.НайтиПоИдентификатору(Идентификатор);

ИндексСтроки = Объект.Товары.Индекс(СтрокаКоллекции);

КонецЕсли;

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

Порядок действий: индекс текущей строки на форме
  1. 1
    Получите ТекущиеДанные через Элементы.<ИмяТаблицы>.ТекущиеДанные
  2. 2
    Проверьте, что данные не равны Неопределено (пользователь мог не выбрать строку)
  3. 3
    Возьмите НомерСтроки и вычтите 1, либо найдите строку по идентификатору через НайтиПоИдентификатору() и примените Индекс()
  4. 4
    Проверьте результат: индекс должен быть в диапазоне от 0 до Количество()-1

Выбор способа под конкретную задачу

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

СитуацияСпособОграничение
Строка уже есть в переменнойКоллекция.Индекс(Строка)Строка должна принадлежать этой коллекции
Поиск по значению колонкиНайти() + Индекс()Возвращает только первую найденную строку
Поиск по нескольким условиямНайтиСтроки() + Индекс()Требуется обход массива результатов
Индекс каждой строки в обходеЦикл Для Индекс = 0 По Количество()-1Медленнее цикла «Для Каждого» на больших таблицах
Текущая строка таблицы формыТекущиеДанные.НомерСтроки - 1Только для табличных частей с нумерацией
Таблица значений на формеНайтиПоИдентификатору() + Индекс()Работает с ДанныеФормыКоллекция

Сравнение вариантов

Индекс() по объекту строки

Быстрый и прямой способ, но требует, чтобы строка принадлежала именно этой коллекции — иначе вернёт -1.

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

Подходит, когда индекс нужен для каждой строки при обходе; на очень больших таблицах обращение по индексу медленнее, чем Для Каждого.

НомерСтроки - 1

Простейший вариант для табличных частей, но ломается, если нумерация строк была нарушена ручным изменением.

Проверка результата и типичные ошибки

Любое получение индекса должно завершаться проверкой результата. Минимальный контроль: индекс не равен -1 и не превышает Количество() - 1. Обращение к несуществующему индексу вызывает исключение и прерывает выполнение кода.

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

Вторая распространённая проблема — работа с копией таблицы. Если таблица значений передана в процедуру по значению или получена через Выгрузить(), строки в ней — новые объекты, и Индекс() для строки исходной таблицы вернёт -1. Ориентируйтесь на поиск по значениям ключевых колонок, а не на ссылку на объект строки.

Типичные ошибки
01
Смешение индекса (с 0) и НомерСтроки (с 1) — смещение на одну позицию и обращение не к той строке
02
Отсутствие проверки на -1 после Индекс() — ошибка «Индекс находится за границами массива»
03
Удаление строк в прямом цикле по индексам — пропуск строк из-за сдвига нумерации
04
Использование индекса, полученного до сортировки таблицы, — обращение уже не к той строке
05
Попытка получить индекс строки динамического списка — у динамического списка нет индексов, есть только ссылки на объекты

Практические приёмы и ограничения

Если индекс нужен для того, чтобы запомнить позицию и вернуться к строке позже, надёжнее запоминать не число, а саму строку или её идентификатор (для данных формы — результат ПолучитьИдентификатор()). Числовой индекс «протухает» при любом изменении состава коллекции, а ссылка на объект строки остаётся валидной, пока строка не удалена.

Для установки активной строки на форме после программного поиска используется обратная схема: найти строку в данных формы, получить её идентификатор и присвоить его свойству ТекущаяСтрока элемента таблицы. Индекс при этом вообще не нужен — платформа работает через идентификаторы.

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

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

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

Что вернёт метод Индекс(), если строки нет в таблице?

Метод вернёт -1. Это штатное поведение, а не ошибка: значение -1 нужно обрабатывать в коде как признак того, что переданная строка не принадлежит коллекции. Обращение к таблице по индексу -1 уже вызовет исключение.

Чем индекс отличается от номера строки в табличной части?

Индекс — внутренняя позиция в коллекции, начинается с 0 и используется в программном коде. НомерСтроки — видимый пользователю номер, начинается с 1 и отображается в колонке «№». Для сообщений пользователю показывайте номер строки, для обращения к коллекции — индекс.

Как получить индекс строки в динамическом списке?

Напрямую — никак: динамический список не является упорядоченной коллекцией с индексами. Свойство ТекущаяСтрока содержит ссылку на объект. Если нужна порядковая позиция записи при заданной сортировке, её можно вычислить только запросом с подсчётом предшествующих записей, что на больших списках затратно.

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

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

Как безопасно удалить несколько строк по индексам?

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

Можно ли получить индекс текущей строки без обращения к данным формы?

Для табличной части — да, через Элементы.<ИмяТаблицы>.ТекущиеДанные.НомерСтроки минус единица. Для таблицы значений на форме номера строк нет, поэтому потребуется НайтиПоИдентификатору() и метод Индекс() коллекции данных формы.

Итог

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

Индекс строки получают методом Индекс() коллекции; нумерация индексов начинается с 0, а НомерСтроки — с 1. Всегда проверяйте результат на -1, не используйте индексы после сортировки или удаления строк, а на форме работайте через идентификаторы строк и НайтиПоИдентификатору().