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