В процессе разработки на платформе 1С:Предприятие часто возникает задача проверки вхождения определенного значения в массив или другую коллекцию данных. Например, нам нужно быстро узнать, присутствует ли конкретный счет учета в списке разрешенных счетов, или проверить наличие числового идентификатора в выборке.
В этой статье мы подробно рассмотрим базовые механизмы поиска элементов в массиве, проанализируем типичную ошибку начинающих разработчиков при поиске в результатах выборки из таблицы значений, а также разберем альтернативные, более быстрые способы проверки вхождения элементов для оптимизации производительности.
Для стандартных массивов, содержащих простые типы данных (числа, строки, даты) или ссылки на объекты базы данных (справочники, документы, планы счетов), в платформе 1С предусмотрен встроенный метод Найти().
Синтаксис метода выглядит следующим образом:
Индекс = Массив.Найти(ИскомоеЗначение);
Метод возвращает Индекс найденного элемента (нумерация начинается с 0), если элемент присутствует в массиве. Если элемент не найден, метод возвращает значение Неопределено.
Рассмотрим практический пример проверки вхождения счета в массив:
// Создаем и заполняем массив счетами учета
СчетаМассив = Новый Массив;
СчетаМассив.Добавить(ПланыСчетов.Хозрасчетный.ТоварыНаСкладах); // 41.01
СчетаМассив.Добавить(ПланыСчетов.Хозрасчетный.Материалы); // 10.01
// Определяем счет, который нужно проверить
ИскомыйСчет = ПланыСчетов.Хозрасчетный.Материалы;
// Проверяем вхождение
Если СчетаМассив.Найти(ИскомыйСчет) <> Неопределено Тогда
Сообщить("Счет найден в массиве, доступ разрешен.");
Иначе
Сообщить("Счет не найден в массиве!");
КонецЕсли;
Важные особенности работы метода Найти():
"10.01", а вы попытаетесь найти число 10.01, метод вернет Неопределено.Очень часто разработчики сталкиваются с ситуацией, когда метод Найти() неожиданно возвращает Неопределено, хотя визуально искомое значение присутствует в коллекции. Разберем этот сценарий подробнее.
Представим ситуацию: у нас есть ТаблицаЗначений с колонкой "КодТовара". Мы пытаемся найти строки по определенному условию с помощью метода НайтиСтроки(), а затем хотим проверить, есть ли в полученном результате конкретный код:
// Выполняем поиск строк по отбору
ПараметрыОтбора = Новый Структура("Статус", "Активен");
НайденныеСтроки = ТаблицаЗначений.НайтиСтроки(ПараметрыОтбора);
// Пытаемся найти конкретный код товара в полученном результате
// ВНИМАНИЕ: Этот код работать НЕ БУДЕТ!
Индекс = НайденныеСтроки.Найти(2550);
Выясним причину, почему этот код не работает:
Метод НайтиСтроки() возвращает объект типа Массив. Однако элементами этого массива являются не числа и не строки из колонок таблицы, а объекты типа СтрокаТаблицыЗначений. Каждый элемент такого массива — это сложная структура данных (вся строка целиком).
Когда мы передаем число 2550 в метод Найти() для этого массива, платформа пытается сравнить число со строкой таблицы значений как с объектом. Естественно, сравнение возвращает ложь, и поиск выдает Неопределено.
Если вам необходимо проверить наличие значения в таблице или в ее подмножестве, используйте один из следующих корректных подходов.
Вариант А. Выгрузка конкретной колонки в "плоский" массив
Если вам нужно получить простой массив, состоящий только из значений нужной колонки (без объектной обертки строк), выгрузите эту колонку отдельно:
// Выгружаем все значения колонки "КодТовара" в простой массив
МассивКодов = ТаблицаЗначений.ВыгрузитьКолонку("КодТовара");
// Теперь в массиве лежат чистые числа/строки, поиск отработает корректно
Индекс = МассивКодов.Найти(2550);
Если Индекс <> Неопределено Тогда
Сообщить("Код найден! Номер строки в исходной таблице: " + Индекс);
КонецЕсли;
Вариант Б. Использование метода Найти() самой ТаблицыЗначений
Если вам не требуется предварительный сложный отбор строк, гораздо эффективнее использовать штатный метод поиска по всей таблице значений:
// Ищем непосредственно в таблице значений, указав имя колонки
НайденнаяСтрока = ТаблицаЗначений.Найти(2550, "КодТовара");
Если НайденнаяСтрока <> Неопределено Тогда
Сообщить("Значение успешно найдено в таблице!");
// Мы можем сразу обратиться к данным найденной строки:
Сообщить("Наименование товара: " + НайденнаяСтрока.Наименование);
КонецЕсли;
Обратите внимание на производительность алгоритмов. Метод Массив.Найти() осуществляет последовательный линейный обход всех элементов (сложность $O(N)$). Если ваш массив содержит десятки тысяч элементов, а проверка выполняется в цикле многократно, это приведет к серьезному замедлению работы программы.
Для быстрой проверки вхождения элемента в список рекомендуется использовать объект Соответствие (аналог Map/Хэш-таблицы). Поиск ключа в объекте Соответствие работает практически мгновенно (сложность близка к $O(1)$), независимо от размера коллекции.
Проанализируем пример оптимизации:
// Вместо массива используем Соответствие
БыстрыйРеестр = Новый Соответствие;
БыстрыйРеестр.Вставить("10.01", Истина);
БыстрыйРеестр.Вставить("10.02", Истина);
БыстрыйРеестр.Вставить("10.03", Истина);
// Проверяемый элемент
ИскомыйСчет = "10.01";
// Моментальная проверка вхождения без перебора всей коллекции
Если БыстрыйРеестр[ИскомыйСчет] <> Неопределено Тогда
Сообщить("Элемент входит в реестр!");
КонецЕсли;
Если вы все же работаете с большими таблицами значений и часто используете методы Найти() или НайтиСтроки(), не забывайте добавлять индексы для колонок, по которым производится поиск. Это переведет поиск на использование быстрых алгоритмов индексирования платформы:
// Добавление индекса существенно ускоряет поиск по колонке
ТаблицаЗначений.Индексы.Добавить("КодТовара");