Есть ли элемент в массиве: Найти(), циклы, Соответствие и запрос во встроенном языке 1С
Содержание
Чтобы проверить, входит ли значение в массив 1С, вызовите метод Найти(): Если МойМассив.Найти(Значение) <> Неопределено Тогда. Метод возвращает индекс первого найденного элемента или Неопределено, если значения в массиве нет. Сравнивать результат нужно именно с Неопределено, потому что индекс первого элемента равен нулю.
Этого хватает в девяти задачах из десяти. Остальные способы нужны, когда сравнение «не точное» (регистр букв, условие по реквизиту), когда проверок тысячи и линейный поиск тормозит, или когда данные всё равно уходят в запрос.
Метод Найти(): базовая проверка вхождения значения в массив
Найти() есть у Массив и ФиксированныйМассив. Он идёт по элементам с начала и останавливается на первом совпадении.
Коды = Новый Массив;
Коды.Добавить("A01");
Коды.Добавить("B02");
Коды.Добавить("C03");
Индекс = Коды.Найти("B02"); // 1
Нет = Коды.Найти("D04"); // Неопределено
Если Коды.Найти("A01") <> Неопределено Тогда
Сообщить("Код есть в списке");
КонецЕсли;
Как Найти() сравнивает значения:
- 🔢 числа, строки, даты, булево — по значению (
"A01"равно"A01"); - 🔗 ссылки на справочники и документы — тоже по значению: две ссылки на один элемент равны;
- 📦 объекты, структуры, массивы, таблицы — по экземпляру: две разные структуры с одинаковыми полями не равны;
- 🔤 строки — с учётом регистра:
"a01"и"A01"дляНайти()разные значения.
Типы не приводятся. Число 5 и строка "5" — разные значения, Найти("5") в массиве чисел вернёт Неопределено.
Классическая ошибка — писать Если Коды.Найти(Х) Тогда или Если Коды.Найти(Х) > 0 Тогда. Первый вариант не работает: результат не булево значение. Второй пропускает элемент с индексом 0, а при отсутствии значения сравнивает Неопределено с числом и падает с ошибкой.
Если нужна только проверка, а индекс не важен, удобно завернуть вызов в функцию:
Функция ЕстьВМассиве(Массив, Значение) Экспорт
Возврат Массив.Найти(Значение) <> Неопределено;
КонецФункции
Перебор массива циклом Для Каждого, когда нужно условие
Найти() ищет только точное равенство. Если вхождение определяется условием, нужен цикл. Примеры таких условий:
- 📅 есть ли дата внутри периода, а не конкретная дата;
- 🧾 есть ли в массиве документ с суммой больше 100 000;
- 🧩 есть ли структура, у которой поле
Кодравно заданному.
Функция ЕстьСтруктураСКодом(МассивСтруктур, ИскомыйКод)
Для Каждого Элемент Из МассивСтруктур Цикл
Если Элемент.Код = ИскомыйКод Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
Выход через Возврат или Прервать обязателен. Без него цикл дойдёт до конца массива, даже если совпадение было в первом элементе.
Если нужен ещё и номер элемента, используйте цикл по индексу:
Для Индекс = 0 По Массив.ВГраница() Цикл
Если Массив[Индекс] > 1000 Тогда
НайденныйИндекс = Индекс;
Прервать;
КонецЕсли;
КонецЦикла;
ВГраница() для пустого массива возвращает -1, поэтому цикл просто не выполнится — отдельная проверка на пустоту не нужна.
💡 Цикл и Найти() работают одинаково по скорости — оба проходят массив от начала. Цикл чуть медленнее на каждом шаге, потому что условие проверяет интерпретатор, а Найти() выполняется внутри платформы. На массивах до нескольких тысяч элементов разницы не заметно.
Как проверить вхождение строки без учёта регистра
Пользователь ввёл «ооо ромашка», а в массиве лежит «ООО Ромашка». Найти() скажет, что значения нет. Есть два рабочих решения.
Привести обе стороны к одному регистру в цикле
Функция ЕстьСтрокаБезРегистра(МассивСтрок, Строка)
Искомое = ВРег(СокрЛП(Строка));
Для Каждого Элемент Из МассивСтрок Цикл
Если ВРег(СокрЛП(Элемент)) = Искомое Тогда
Возврат Истина;
КонецЕсли;
КонецЦикла;
Возврат Ложь;
КонецФункции
ВРег() переводит строку в верхний регистр, НРег() — в нижний. Разницы, какой выбрать, нет; главное — одинаково для обеих сторон. СокрЛП() убирает пробелы по краям: при загрузке из Excel они встречаются постоянно.
Подготовить нормализованную копию один раз
Если проверок много, приводить к регистру каждый элемент при каждом поиске расточительно. Сделайте копию массива в верхнем регистре один раз, а дальше ищите через Найти():
Нормализованный = Новый Массив;
Для Каждого Элемент Из Исходный Цикл
Нормализованный.Добавить(ВРег(СокрЛП(Элемент)));
КонецЦикла;
Если Нормализованный.Найти(ВРег(СокрЛП(Ввод))) <> Неопределено Тогда
// значение найдено
КонецЕсли;
Индексы в копии совпадают с исходными, так что по найденному индексу можно взять оригинальное значение из Исходный.
Не склеивайте массив в одну строку через СтрСоединить() и не ищите по ней СтрНайти(). Поиск «12» найдёт его внутри «312», а разделитель может встретиться в самих значениях. Такие проверки дают ложные срабатывания, которые трудно отловить.
Соответствие и Структура вместо массива при частых проверках
Массив хорош для хранения по порядку, но плох для вопроса «есть ли такое значение». Каждая проверка — это проход по элементам. Если массив из 10 000 значений сверяется с другим списком из 10 000 значений, получается до 100 млн сравнений.
Соответствие хранит ключи в хэш-таблице. Проверка ключа почти не зависит от размера коллекции.
// Готовим набор один раз
Набор = Новый Соответствие;
Для Каждого Значение Из БольшойМассив Цикл
Набор.Вставить(Значение, Истина);
КонецЦикла;
// Проверяем сколько угодно раз
Если Набор[Проверяемое] <> Неопределено Тогда
// значение входит в набор
КонецЕсли;
Обращение Набор[Ключ] к отсутствующему ключу возвращает Неопределено, ошибки не будет. То же самое делает Набор.Получить(Ключ). Повторная вставка того же ключа не создаёт дубль, поэтому заодно получается список уникальных значений.
Для поиска без учёта регистра кладите в соответствие ключ ВРег(Значение), а в значение — исходную строку. Тогда проверка и получение «как было записано» делаются одним обращением.
Когда подойдёт Структура
Структура тоже проверяет ключ быстро: метод Свойство("Имя") возвращает Истина или Ложь. Но у неё жёсткое ограничение — ключ должен быть корректным идентификатором языка. Строка с пробелом, дефисом или начинающаяся с цифры ключом быть не может, Вставить() вызовет ошибку.
| Коллекция | Что может быть ключом | Проверка вхождения | Порядок элементов |
|---|---|---|---|
Массив | ключей нет | Найти(), проход по всем элементам | сохраняется |
Соответствие | любое значение: число, строка, дата, ссылка | [Ключ] <> Неопределено, быстро | не гарантируется |
Структура | только имя-идентификатор | Свойство(Имя), быстро | сохраняется |
СписокЗначений | ключей нет | НайтиПоЗначению(), проход | сохраняется, можно сортировать |
Структура уместна для фиксированного набора имён — например, разрешённых действий или настроек. Для данных из базы и пользовательского ввода берите Соответствие.
Массивы ссылок, объектов и составных значений
Самый частый практический случай — массив ссылок: выделенные строки списка, результат ВыгрузитьКолонку(), отобранные документы. Здесь Найти() работает надёжно, потому что ссылки сравниваются по значению.
Выбранные = ТаблицаТоваров.ВыгрузитьКолонку("Номенклатура");
Если Выбранные.Найти(ТекущаяНоменклатура) <> Неопределено Тогда
// товар уже есть в табличной части
КонецЕсли;
Ловушки, на которых спотыкаются:
- 🧱 Объект вместо ссылки.
СправочникОбъектиДокументОбъектсравниваются как экземпляры. Два объекта, прочитанные из одной ссылки черезПолучитьОбъект(), не равны. Храните в массиве ссылки, а не объекты. - 🕳️ Пустая ссылка и Неопределено.
Справочники.Номенклатура.ПустаяСсылка()иНеопределено— разные значения. Если массив собран из реквизита составного типа, в нём могут оказаться оба. - 🔀 Разные типы с одинаковым представлением. Ссылка на контрагента и строка с его наименованием на экране выглядят одинаково, но
Найти()их не сравнит. - 🕐 Даты со временем.
'20260115000000'и'20260115093000'разные. Если важен только день, приведите значения черезНачалоДня()при наполнении массива и перед поиском.
Проверка на клиенте
В клиентском модуле ссылки доступны, и Найти() по массиву ссылок работает так же. Обращение к реквизиту ссылки через точку (Ссылка.Код) на клиенте недоступно. Поэтому если условие проверки — реквизит, нужный реквизит соберите на сервере и передайте на клиент вместе с массивом.
Большие массивы: бинарный поиск и хэш-таблица
Для массивов в десятки тысяч элементов есть два способа ускорить проверку.
Хэш-таблица — это и есть Соответствие из раздела выше. Подготовка — один проход, дальше проверки почти мгновенные. Для большинства задач это лучший вариант.
Бинарный поиск работает на отсортированном массиве. На каждом шаге он отбрасывает половину оставшихся элементов: для 1 000 000 значений хватает около 20 сравнений. В платформе готовой функции нет, пишется вручную:
Функция НайтиБинарно(УпорядоченныйМассив, Значение)
Лево = 0;
Право = УпорядоченныйМассив.ВГраница();
Пока Лево <= Право Цикл
Середина = Цел((Лево + Право) / 2);
Текущее = УпорядоченныйМассив[Середина];
Если Текущее = Значение Тогда
Возврат Середина;
ИначеЕсли Текущее < Значение Тогда
Лево = Середина + 1;
Иначе
Право = Середина - 1;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
У Массив нет метода сортировки. Отсортировать можно через список значений:
Список = Новый СписокЗначений;
Список.ЗагрузитьЗначения(Числа);
Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
Отсортированный = Список.ВыгрузитьЗначения();
Бинарный поиск надёжен для чисел и дат. Ссылки нельзя сравнивать операторами < и >, а для строк порядок сортировки списка может не совпасть с результатом оператора <. Для ссылок и строк используйте Соответствие.
Сравнение подходов по скорости:
| Способ | Подготовка | Одна проверка | Когда выбирать |
|---|---|---|---|
Найти() | не нужна | проход по массиву | разовые проверки, массив до нескольких тысяч |
| Цикл с условием | не нужна | проход по массиву | проверка по полю или диапазону |
Соответствие | один проход | почти мгновенно | много проверок, любые типы ключей |
| Бинарный поиск | сортировка | ~log₂ N сравнений | отсортированные числа и даты |
Помните про цену подготовки. Если проверка одна, строить соответствие или сортировать массив дольше, чем просто вызвать Найти().
Массив.Найти(Значение) <> Неопределено — без подготовки и лишнего кода.Для Каждого с условием и выходом через Возврат при первом совпадении.Соответствие, дальше проверять Набор[Значение] <> Неопределено.В (&Массив) прямо в запросе.Чем заменить LINQ: запрос, БСП и другие коллекции
Во встроенном языке 1С нет LINQ, лямбда-выражений и методов-расширений для массивов. Нельзя написать что-то вроде Массив.Any(...). Задачи, которые в других языках решает LINQ, в 1С закрывают иначе.
Проверка в запросе
Если массив нужен для отбора данных из базы, не проверяйте вхождение в цикле по выборке. Передайте массив параметром:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура,
| Остатки.КоличествоОстаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
|ГДЕ
| Остатки.Номенклатура В (&СписокТоваров)";
Запрос.УстановитьПараметр("СписокТоваров", МассивТоваров);
Условие В (&Параметр) принимает массив, список значений или фиксированный массив. Работу выполняет СУБД, и это быстрее любого перебора в коде. Имя регистра в примере условное: подставьте регистр своей конфигурации.
Функции БСП
В типовых конфигурациях на Библиотеке стандартных подсистем есть общий модуль ОбщегоНазначенияКлиентСервер с функциями для массивов. Например, ДополнитьМассив() с параметром добавления только уникальных значений и СвернутьМассив() для удаления дублей. Отдельной функции «содержит ли массив значение» там нет: для этого и так достаточно Найти().
Таблица значений с индексом
Если элементы — это строки с несколькими полями, удобнее ТаблицаЗначений. Метод Найти(Значение, "Колонка") возвращает первую подходящую строку или Неопределено. НайтиСтроки(Новый Структура("Колонка", Значение)) возвращает массив строк. Добавьте индекс через Индексы.Добавить("Колонка"), и повторные поиски по этой колонке ускорятся.
Можно ли найти все вхождения значения, а не только первое?
Найти() возвращает только первый индекс. Чтобы получить все, пройдите массив циклом по индексу и собирайте номера совпадений в отдельный массив.Работает ли Найти() для значения Неопределено или Null?
Массив.Найти(Неопределено) вернёт индекс первого элемента со значением Неопределено. Null и Неопределено при этом разные значения.Как проверить, что все значения одного массива есть в другом?
Соответствие из второго массива, затем пройдите первый массив и выйдите с результатом Ложь на первом значении, которого нет в соответствии.Чем проверить значение в списке значений?
СписокЗначений метод НайтиПоЗначению(). Он возвращает элемент списка или Неопределено, а не индекс. Индекс элемента даёт метод Индекс() самого списка.Почему Найти() не находит число, которое точно есть в массиве?
Число() или Строка() при заполнении массива.