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