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