К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как проверить массив на дубли в 1С

Как проверить массив на дубли в 1С: поиск через Найти, соответствие, таблицу значений и запрос. Примеры кода, сравнение способов, типичные ошибки и ответы на вопросы.

Проверка массива на дубли — типовая задача при разработке на платформе 1С:Предприятие. Дубли появляются при объединении данных из разных источников: результатов запросов, выгрузок, табличных частей документов, отборов пользователя. Если не отсеять повторы, дальше по цепочке возникают ошибки — задвоенные строки в отчётах, повторные проведения документов, неверные итоги.

Встроенной функции «найти дубликаты в массиве» во встроенном языке 1С нет, поэтому проверку реализуют вручную. Есть четыре рабочих подхода: перебор с методом Найти(), свёртка через соответствие, свёртка через таблицу значений и проверка запросом. Выбор зависит от размера массива, типа элементов и того, что нужно получить — факт наличия дублей, их список или очищенный массив.

Ниже разобраны все способы с готовым кодом, критерии выбора, ограничения и типичные ошибки. Примеры приведены для управляемого приложения, но работают и в обычных формах.

Как проверить массив на дубли через соответствие

Соответствие — самый быстрый и универсальный инструмент. Идея: ключи соответствия всегда уникальны, поэтому повторная вставка того же значения сразу выявляет дубль. Поиск по ключу в соответствии выполняется быстро даже на десятках тысяч элементов, в отличие от метода Найти() массива, который каждый раз перебирает элементы заново.

Функция ниже возвращает Истина, если в массиве есть хотя бы один дубль:

Функция ЕстьДубли(Массив)

Проверка = Новый Соответствие;

Для Каждого Элемент Из Массив Цикл

Если Проверка[Элемент] <> Неопределено Тогда

Возврат Истина; // дубль найден

КонецЕсли;

Проверка.Вставить(Элемент, Истина);

КонецЦикла;

Возврат Ложь;

КонецФункции

Если нужен не факт, а список повторяющихся значений, дубль сохраняют в отдельный массив:

Функция НайтиДубли(Массив)

Проверка = Новый Соответствие;

Дубли = Новый Массив;

Для Каждого Элемент Из Массив Цикл

Если Проверка[Элемент] = Неопределено Тогда

Проверка.Вставить(Элемент, 1);

Иначе

Если Проверка[Элемент] = 1 Тогда

Дубли.Добавить(Элемент);

Проверка.Вставить(Элемент, 2); // чтобы дубль не попал в список дважды

КонецЕсли;

КонецЕсли;

КонецЦикла;

Возврат Дубли;

КонецФункции

Ограничение способа: значения сравниваются по равенству, поэтому важен тип данных. Строка "001" и число 1 — разные ключи, а ссылки на один и тот же объект — одинаковые. Пустые значения (Неопределено, пустые ссылки) тоже участвуют в сравнении; если они допустимы в данных, их стоит пропускать отдельным условием.

Проверка перебором с методом Найти()

Самый очевидный вариант — для каждого элемента проверять, встречается ли он ещё раз в массиве через Массив.Найти(Элемент). Код короткий и читаемый, подходит для небольших массивов и разовых проверок.

Для Индекс = 0 По Массив.Количество() - 1 Цикл

Элемент = Массив[Индекс];

Если Массив.Найти(Элемент) <> Индекс Тогда

Сообщить("Дубль: " + Элемент);

КонецЕсли;

КонецЦикла;

Логика такая: Найти() возвращает индекс первого вхождения. Если он не совпадает с текущим индексом, значит, элемент уже встречался раньше — это дубль.

Главный недостаток — производительность. На каждой итерации выполняется линейный поиск, и на массиве в несколько тысяч элементов проверка начинает заметно тормозить. Поэтому метод Найти() оправдан только для массивов до пары сотен элементов или когда важна простота кода, а не скорость.

Удаление дублей через таблицу значений

Если задача — не просто найти повторы, а получить массив без дублей, удобно свернуть данные через таблицу значений. Метод Свернуть() группирует строки по указанным колонкам и автоматически убирает повторы.

Функция МассивБезДублей(Массив)

ТЗ = Новый ТаблицаЗначений;

ТЗ.Колонки.Добавить("Значение");

Для Каждого Элемент Из Массив Цикл

Строка = ТЗ.Добавить();

Строка.Значение = Элемент;

КонецЦикла;

ТЗ.Свернуть("Значение");

Возврат ТЗ.ВыгрузитьКолонку("Значение");

КонецФункции

Чтобы заодно узнать, какие значения дублировались, добавьте колонку-счётчик и суммируйте её при свёртке:

ТЗ.Колонки.Добавить("Количество");

// при добавлении строки: Строка.Количество = 1;

ТЗ.Свернуть("Значение", "Количество");

Для Каждого Строка Из ТЗ Цикл

Если Строка.Количество > 1 Тогда

Сообщить("Дубль: " + Строка.Значение + " — " + Строка.Количество + " раз(а)");

КонецЕсли;

КонецЦикла;

Дополнительный плюс: после свёртки таблицу можно отсортировать, что иногда важно для дальнейшей обработки. Минус — лишнее копирование данных и чуть больше кода, чем у варианта с соответствием.

Проверка дублей запросом по данным массива

Когда массив содержит ссылки или значения, по которым нужно проверить дубли в базе данных (а не внутри самого массива), правильное решение — запрос. Например, проверить, есть ли в справочнике элементы с повторяющимся кодом из списка, или найти документы, где один контрагент встречается несколько раз.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Контрагенты.ИНН КАК ИНН,

| КОЛИЧЕСТВО(Контрагенты.Ссылка) КАК Количество

|ИЗ

| Справочник.Контрагенты КАК Контрагенты

|ГДЕ

| Контрагенты.Ссылка В (&МассивСсылок)

|СГРУППИРОВАТЬ ПО

| Контрагенты.ИНН

|ИМЕЮЩИЕ

| КОЛИЧЕСТВО(Контрагенты.Ссылка) > 1";

Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);

Результат = Запрос.Выполнить();

Массив передаётся в параметр и используется в условии В (&МассивСсылок) — это стандартный приём. Группировка с ИМЕЮЩИЕ КОЛИЧЕСТВО(...) > 1 возвращает только повторяющиеся значения. Если результат пустой — дублей нет.

Запросный способ незаменим, когда критерий дубля — не сам элемент массива, а его реквизит (ИНН, наименование, номер документа). Пытаться решить такую задачу перебором с обращением к реквизитам каждой ссылки — значит выполнить сотни обращений к базе вместо одного запроса.

Сравнение способов

Соответствие

Быстро на любом объёме, подходит для поиска факта дублей и их списка. Требует аккуратности с типами значений.

Найти() в цикле

Простейший код для маленьких массивов. Медленный на больших объёмах из-за линейного поиска на каждой итерации.

Таблица значений

Лучший вариант, когда нужен очищенный массив или количество повторов каждого значения. Чуть больше кода и памяти.

Запрос

Единственно правильный путь, если дубли проверяются по реквизитам ссылок в базе данных. Не работает с произвольными значениями вне БД.

Как выбрать способ под свою задачу

Выбор определяется тремя вопросами: что за данные в массиве, какой результат нужен и какой объём обрабатывается. Простая памятка помогает не изобретать велосипед.

Маршрут решения

Как выбрать действие

Нужно только узнать, есть ли дубли, массив любого размераИспользуйте соответствие с выходом из цикла при первом дубле
Нужен массив без повторовСверните данные через таблицу значений и выгрузите колонку
Нужен список дублей с количеством повторовТаблица значений со свёрткой по значению и суммой счётчика
Дубль определяется реквизитом ссылки (ИНН, код, номер)Проверяйте запросом с группировкой и ИМЕЮЩИЕ
Массив маленький, код пишется один разДопустим простой перебор с Найти()

Отдельный случай — массивы структур или других составных объектов. Такие элементы нельзя использовать как ключи соответствия и сравнивать напрямую. Здесь дубль определяют по набору полей: формируют строковый ключ вида Структура.Поле1 + "|" + Структура.Поле2 и проверяют его, или сворачивают таблицу значений по нескольким колонкам сразу.

Проверка результата и типичные ошибки

После внедрения проверки протестируйте её на контрольных наборах: пустом массиве, массиве из одного элемента, массиве без дублей, массиве, где все элементы одинаковые, и массиве со смешанными типами. Все пять случаев должны обрабатываться без ошибок и с ожидаемым результатом.

Типичные ошибки
01
{'name': 'Сравнение значений разных типов', 'text': 'Число 1 и строка "1" считаются разными элементами — дубль не находится. Приводите значения к одному типу перед проверкой.'}
02
{'name': 'Проверка дублей ссылок через обращение к реквизитам в цикле', 'text': 'Каждое обращение к реквизиту ссылки — отдельный запрос к базе. На сотнях элементов обработка резко замедляется; используйте один запрос.'}
03
{'name': 'Удаление элементов из массива внутри цикла по нему', 'text': 'Индексы смещаются, элементы пропускаются или возникает ошибка выхода за границу. Удаляйте с конца массива или собирайте новый массив.'}
04
{'name': 'Игнорирование пустых значений', 'text': 'Неопределено и пустые ссылки участвуют в сравнении как обычные значения и дают ложные дубли. Отфильтруйте их заранее, если они допустимы.'}

Ещё одна тонкость — регистр строк. Для соответствия и таблицы значений "Номенклатура" и "номенклатура" — разные значения. Если бизнес-логика считает их дублями, приводите строки к одному регистру через ВРег() или НРег() перед проверкой.

Контрольные точки

Как проверить результат

Массив без повторов после обработки
Норма

Количество элементов равно количеству уникальных значений

Тревожный признак

Размер не изменился, хотя дубли точно были

Список найденных дублей
Норма

Каждый дубль присутствует один раз, количество совпадает с ручной проверкой

Тревожный признак

Одно значение повторяется в списке дублей несколько раз

Производительность
Норма

Проверка массива из тысяч элементов выполняется мгновенно

Тревожный признак

Заметная пауза — вероятно, используется Найти() в цикле

Работа с типами
Норма

Числа и строки с одинаковым содержимым обрабатываются осознанно

Тревожный признак

Результат зависит от того, откуда пришли данные

Частые вопросы

Можно ли проверить на дубли массив структур?

Напрямую — нет: структуры нельзя сравнивать на равенство и использовать как ключи соответствия. Сформируйте строковый ключ из значащих полей структуры и проверяйте его, либо выгрузите поля в таблицу значений и сверните по всем колонкам.

Как удалить дубли из массива, не создавая новый?

Проходите по массиву циклом с конца к началу (от Количество()-1 до 0) и удаляйте элемент, если его значение уже есть в контрольном соответствии. Обратный порядок нужен, чтобы удаление не сдвигало индексы ещё не проверенных элементов.

Что быстрее — соответствие или таблица значений?

Для простой проверки факта дубля соответствие обычно предпочтительнее: меньше накладных расходов на создание структуры данных. Таблица значений выигрывает, когда нужен дополнительный результат — количество повторов, сортировка или свёртка по нескольким полям.

Как проверить дубли строк без учёта регистра?

Перед вставкой в соответствие или таблицу приведите строку к одному регистру функцией ВРег() или НРег(). Тогда "Абв" и "АБВ" будут распознаны как один и тот же элемент.

Можно ли передать массив в запрос и проверить дубли внутри него средствами запроса?

Массив можно передать в параметр и поместить во временную таблицу через конструкцию ВЫБРАТЬ ... ПОМЕСТИТЬ с условием В (&Массив), но проще и надёжнее сначала выгрузить массив в таблицу значений, передать её как параметр и сгруппировать. Так запрос остаётся читаемым и работает с любыми типами значений.

Почему соответствие не находит дубль, хотя значения визуально одинаковые?

Чаще всего причины три: разные типы значений (число и строка), неразрывные пробелы или невидимые символы в строках, разный регистр букв. Проверьте типы через ТипЗнч() и очистите строки функцией СокрЛП() перед сравнением.

Итог

Что важно запомнить

Для поиска дублей в массиве 1С используйте соответствие — это быстро и универсально. Для получения очищенного массива сворачивайте данные через таблицу значений, а дубли по реквизитам ссылок проверяйте запросом. Следите за типами значений, регистром строк и пустыми элементами — они главный источник ложных результатов.