В практике разработки на платформе 1С:Предприятие часто возникает ситуация, когда нам необходимо сопоставить данные, полученные из запроса, со строками существующей табличной части документа или обработки. Одной из самых коварных и распространенных ошибок, с которой сталкиваются новички, является попытка напрямую обратиться к реквизиту строки после использования метода НайтиСтроки(). В этой статье мы подробно разберем, почему возникает ошибка «Поле объекта не обнаружено», как правильно работать с коллекциями и какими альтернативными способами можно оптимизировать этот процесс.
Рассмотрим ситуацию, представленную автором вопроса. В коде выполняется запрос к срезу последних регистра сведений для получения цен комплектующих. После получения результата запроса разработчик пытается найти соответствующую строку в табличной части Комплектующие объекта и присвоить ей значение:
ИскомыеСтроки = Объект.Комплектующие.НайтиСтроки(мОтбор);
Если ИскомыеСтроки.Количество() = 1 Тогда
ИскомыеСтроки.Цена = СтрокаЗапроса.Цена; // ОШИБКА ЗДЕСЬ
КонецЕсли;
Давайте выясним причину ошибки. Метод НайтиСтроки() предназначен для поиска всех строк, соответствующих заданному отбору (структуре). Даже если в вашей таблице всего одна подходящая строка, этот метод всегда возвращает Массив. У объекта типа Массив в 1С нет свойства Цена — оно есть только у элементов этого массива (самих строк табличной части). Именно поэтому система выдает исключение, сообщая, что поле не обнаружено.
Если мы точно уверены, что по нашему условию может быть найдена только одна строка (или нам нужна только первая найденная), мы можем обратиться к ней по индексу [0]. Разберем исправленный вариант кода:
ИскомыеСтроки = Объект.Комплектующие.НайтиСтроки(мОтбор);
Если ИскомыеСтроки.Количество() > 0 Тогда
// Обращаемся к ПЕРВОМУ элементу массива найденных строк
ИскомыеСтроки[0].Цена = СтрокаЗапроса.Цена;
КонецЕсли;
Этот способ прост и эффективен, когда уникальность данных в табличной части гарантирована программно. Однако стоит помнить, что использование ИскомыеСтроки[0] без предварительной проверки Количество() > 0 может привести к другой критической ошибке: «Индекс находится за границами массива».
Проанализируем ситуацию, когда в табличной части может быть несколько строк с одной и той же номенклатурой (например, дубли или разные партии). В этом случае правильнее будет обновить цену во всех найденных позициях. Для этого мы воспользуемся циклом Для каждого:
ИскомыеСтроки = Объект.Комплектующие.НайтиСтроки(мОтбор);
Для каждого СтрокаТЧ Из ИскомыеСтроки Цикл
СтрокаТЧ.Цена = СтрокаЗапроса.Цена;
КонецЦикла;
Преимущества данного подхода:
Если поиск производится только по одному полю (например, по Номенклатуре) и нам достаточно найти только первое вхождение, мы рекомендуем использовать метод Найти() вместо НайтиСтроки(). Рассмотрим пример:
НайденнаяСтрока = Объект.Комплектующие.Найти(СтрокаЗапроса.Номенклатура, "Номенклатура");
Если НайденнаяСтрока <> Неопределено Тогда
НайденнаяСтрока.Цена = СтрокаЗапроса.Цена;
КонецЕсли;
В отличие от НайтиСтроки(), метод Найти() возвращает либо конкретный объект (строку таблицы), либо значение Неопределено. Это избавляет нас от необходимости работать с массивами и делает код более лаконичным.
Когда мы выполняем поиск внутри цикла по результату запроса (как в исходном примере), мы создаем алгоритм со сложностью $O(N \times M)$. Если в табличной части 1000 строк и в результате запроса 1000 строк, системе придется выполнить до 1 000 000 операций сравнения. Это может привести к заметным задержкам.
Проанализируем более профессиональный подход с использованием объекта Соответствие. Мы сначала соберем все цены в «словарь», а затем один раз пройдем по табличной части:
// 1. Выгружаем цены в Соответствие для быстрого поиска
ЦеныСоответствие = Новый Соответствие;
РезультатЗапроса = Запрос.Выполнить().Выбрать();
Пока РезультатЗапроса.Следующий() Цикл
ЦеныСоответствие.Вставить(РезультатЗапроса.Номенклатура, РезультатЗапроса.Цена);
КонецЦикла;
// 2. Один раз проходим по табличной части документа
Для каждого СтрокаТЧ Из Объект.Комплектующие Цикл
ЦенаИзЗапроса = ЦеныСоответствие.Получить(СтрокаТЧ.Номенклатура);
Если ЦенаИзЗапроса <> Неопределено Тогда
СтрокаТЧ.Цена = ЦенаИзЗапроса;
КонецЕсли;
КонецЦикла;
Такой метод работает в десятки раз быстрее на больших объемах данных, так как поиск в Соответствии происходит практически мгновенно.
Посмотрим на строку кода автора: Запрос.УстановитьПараметр("СпКомплектующие", Объект.Комплектующие.Выгрузить(,"Номенклатура"));. Метод Выгрузить() возвращает ТаблицуЗначений. Хотя 1С позволяет использовать таблицу из одной колонки в условии запроса В (&Параметр), это считается менее «чистым» подходом.
Мы рекомендуем использовать метод ВыгрузитьКолонку(), который возвращает Массив:
СписокНоменклатуры = Объект.Комплектующие.ВыгрузитьКолонку("Номенклатура");
Запрос.УстановитьПараметр("СпКомплектующие", СписокНоменклатуры);
Это гарантирует правильную типизацию данных внутри запроса и снижает вероятность трудноотловимых ошибок при сравнении ссылочных типов.
Подводя итог, повторим основные правила работы с поиском в коллекциях 1С:
НайтиСтроки() возвращает Массив. Обращаться к свойствам нужно через индекс [0] или в цикле.Найти() — это вернет саму строку или Неопределено.Соответствия.Количество() > 0 или проверку на Неопределено).Соблюдение этих простых правил поможет вам писать надежный, быстрый и понятный код, избегая досадных ошибок типизации.