Почему НайтиСтроки() не работает по НомерСтроки в табличной части 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

Выясняем причину: в чем разница между коллекциями?

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

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

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

Решение 1. Прямое обращение по индексу коллекции (Самый быстрый способ)

Поскольку номер строки — это фактически её порядковый номер в коллекции, смещенный на единицу (так как индексация в 1С начинается с нуля), самым производительным решением будет прямое обращение к строке по индексу. Этот метод выполняется за константное время $O(1)$ и не требует от платформы обхода всей таблицы.

Посмотрим на пример реализации такого обращения:


// Получаем строку напрямую по индексу со смещением
СтрокаТЧ = Объект.Товары[Выборка.НомерСтроки - 1];

// Либо с использованием метода Получить()
СтрокаТЧ = Объект.Товары.Получить(Выборка.НомерСтроки - 1);

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

Решение 2. Выгрузка табличной части в Таблицу значений

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

Рассмотрим, как выглядит этот процесс на практике:


// Выгружаем ТЧ в Таблицу значений, где НомерСтроки станет физической колонкой
ТаблицаТоваров = Объект.Товары.Выгрузить();

// Формируем структуру отбора для поиска
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("НомерСтроки", Выборка.НомерСтроки);

// Выполняем поиск по физической колонке таблицы значений
НайденныеСтроки = ТаблицаТоваров.НайтиСтроки(ПараметрыОтбора);

Если НайденныеСтроки.Количество() > 0 Тогда
    СтрокаТЗ = НайденныеСтроки[0];
    // Выполняем необходимые действия с найденной строкой
КонецЕсли;

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

Решение 3. Использование Ключа связи (Рекомендация для крупных систем)

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

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


ПараметрыОтбора = Новый Структура("КлючСвязи", Выборка.КлючСвязи);
НайденныеСтроки = Объект.Товары.НайтиСтроки(ПараметрыОтбора);

Если НайденныеСтроки.Количество() > 0 Тогда
    СтрокаТовара = НайденныеСтроки[0];
    // Обрабатываем найденную строку
КонецЕсли;

Использование явного ключа связи решает проблему смещения индексов при удалении строк и обеспечивает максимальную стабильность кода при любых интерактивных действиях пользователя.

← На главную