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