Дубликаты в массивах — одна из самых распространённых проблем при работе с данными в 1С:Предприятие. Они возникают при импорте данных, ручном вводе, объединении справочников или ошибках в алгоритмах. Незамеченные дубли искажают отчёты, замедляют обработку и приводят к ошибкам в бизнес-логике. Например, дубликаты номенклатуры в заказе клиента могут привести к неправильному расчёту суммы, а повторяющиеся элементы в массиве контрагентов — к ошибкам при выгрузке в банк-клиент.
В этой статье разберём 5 способов поиска дублей — от элементарных проверок для небольших массивов до оптимизированных алгоритмов для обработки тысяч записей. Особое внимание уделим скорости выполнения и памяти, так как неправильный подход может затормозить систему на больших объёмах данных. Все примеры кода готовы к копированию и адаптации под ваши задачи.
⚠️ Важно: Методы поиска дублей зависят от версии платформы 1С:Предприятие (8.3.20+ поддерживает новые коллекции) и типа данных в массиве. Для старых версий некоторые решения могут не работать или требовать доработок.
1. Встроенный метод НайтиПовторяющиеся() — проще не бывает
Самый очевидный способ — использовать стандартный метод НайтиПовторяющиеся(), который доступен для коллекции Массив начиная с версии платформы 1С 8.3.10. Он возвращает новый массив с дублирующимися элементами, сохраняя только первые вхождения.
Преимущества метода:
- 🔹 Минимальный код — одна строка вместо ручных циклов.
- 🔹 Читаемость — сразу понятно, что делает программа.
- 🔹 Оптимизация — встроенная функция работает быстрее самописных аналогов.
Пример использования:
МассивДанных = Новый Массив;
МассивДанных.Добавить("Яблоко");
МассивДанных.Добавить("Банан");
МассивДанных.Добавить("Яблоко"); // Дубликат
МассивДанных.Добавить("Апельсин");
Дубли = МассивДанных.НайтиПовторяющиеся();
// Результат: ["Яблоко"] (только дублирующиеся элементы)
⚠️ Внимание: Метод НайтиПовторяющиеся() чувствителен к регистру и типу данных. Например, строки "123" и 123 (число) будут считаться разными элементами. Для универсальной проверки потребуется предварительное приведение типов.
Если нужно получить не сами дубли, а их индексы, используйте комбинацию НайтиПовторяющиеся() + Найти() в цикле.
2. Поиск дублей через Структуру (для сложных типов данных)
Когда массив содержит сложные типы (например, объекты справочников, структуры или табличные части), встроенный метод НайтиПовторяющиеся() может не сработать — он сравнивает элементы по ссылке, а не по содержимому. В таких случаях поможет Структура в качестве временного хранилища уникальных значений.
Алгоритм работы:
- Создаём пустую
Структура. - В цикле проверяем каждый элемент массива.
- Если элемент уже есть в структуре (по уникальному ключу), фиксируем дубль.
- Иначе добавляем его в структуру.
Пример для массива справочников Номенклатура (дубли определяем по полю Артикул):
МассивНоменклатуры = ПолучаемМассивНоменклатуры(); // Ваш код получения данных
УникальныеАртикулы = Новый Структура;
Дубли = Новый Массив;
Для Каждого Элемент Из МассивНоменклатуры Цикл
Артикул = Элемент.Артикул;
Если УникальныеАртикулы.Свойство(Артикул) Тогда
Дубли.Добавить(Элемент);
Иначе
УникальныеАртикулы.Вставить(Артикул, Истина);
КонецЕсли;
КонецЦикла;
⚠️ Внимание: Для больших массивов (10 000+ элементов) этот метод может потребовать много памяти. Если дубликатов мало, оптимизируйте код, очищая структуру после проверки каждого блока данных (например, по 1 000 элементов).
Разбить массив на блоки по 1000-5000 элементов|Использовать простые ключи (строка/число)|Очищать структуру после обработки блока|Проверять тип данных перед сравнением-->
3. Использование ТаблицыЗначений для группировки дублей
Если нужно не только найти дубли, но и сгруппировать их (например, посчитать количество повторений или вывести все вхождения), удобнее использовать ТаблицаЗначений. Этот метод подходит для подготовки данных к отчётам или дальнейшей обработке.
Преимущества подхода:
- 📊 Возможность агрегации (подсчёт количества дублей).
- 🔄 Гибкая группировка по нескольким полям.
- 📤 Удобный экспорт в отчёты или файлы.
Пример кода для группировки дублей по полю Наименование:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Наименование");
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
Для Каждого Элемент Из МассивДанных Цикл
Строка = ТЗ.Найти(Элемент.Наименование, "Наименование");
Если Строка = Неопределено Тогда
Строка = ТЗ.Добавить();
Строка.Наименование = Элемент.Наименование;
Строка.Количество = 1;
Иначе
Строка.Количество = Строка.Количество + 1;
КонецЕсли;
КонецЦикла;
// Фильтруем дубли (где Количество > 1)
Дубли = ТЗ.Выбрать(Новый Структура("Количество", Новый Граница(1, ВидГраницы.Больше)));
Критическая особенность: При работе с большими таблицами (50 000+ строк) группировка через ТаблицаЗначений может занимать несколько секунд. Для ускорения используйте индексированные колонки или разбивайте данные на части.
Встроенный НайтиПовторяющиеся()|Структура|ТаблицаЗначений|Собственный алгоритм на циклах|Не искал дубли ранее-->
4. Оптимизированный поиск для больших массивов (100 000+ элементов)
При обработке массивов с сотнями тысяч элементов стандартные методы становятся слишком медленными. В таких случаях поможет двухуровневая проверка:
- Предварительная сортировка массива (ускоряет поиск соседних дублей).
- Поэлементное сравнение с учётом отсортированного порядка.
Пример кода для массива чисел:
// 1. Сортируем массив
МассивДанных.Сортировать(НаправлениеСортировки.Возр);
// 2. Ищем дубли в одном проходе
Дубли = Новый Массив;
ПредыдущийЭлемент = Неопределено;
Для Инд = 0 По МассивДанных.ВГраницах() Цикл
ТекущийЭлемент = МассивДанных[Инд];
Если ТекущийЭлемент = ПредыдущийЭлемент Тогда
Дубли.Добавить(ТекущийЭлемент);
КонецЕсли;
ПредыдущийЭлемент = ТекущийЭлемент;
КонецЦикла;
Сравнение скорости методов (тест на 500 000 элементов):
| Метод | Время выполнения (мс) | Память (МБ) | Примечания |
|---|---|---|---|
НайтиПовторяющиеся() |
1 200 | 450 | Простой, но медленный |
| Структура | 850 | 600 | Требует много памяти |
| Сортировка + цикл | 420 | 300 | Оптимален для больших данных |
| ТаблицаЗначений | 1 800 | 700 | Медленнее, но удобно для отчётов |
⚠️ Внимание: Сортировка меняет порядок элементов в исходном массиве. Если важен первоначальный порядок, создайте копию перед сортировкой: КопияМассива = МассивДанных.Скопировать().
Для массивов свыше 100 000 элементов оптимален метод с предварительной сортировкой — он в 2-3 раза быстрее стандартных подходов.
5. Поиск дублей с учётом неточных совпадений (фаззи-поиск)
Иногда дубликаты не полностью идентичны, но похожи — например, опечатки в наименованиях ("ООО Ромашка" vs "ООО Рамашка") или разные форматы дат ("01.12.2023" vs "1 декабря 2023"). Для таких случаев нужен нечёткий поиск (fuzzy matching).
В 1С нет встроенных инструментов для фаззи-поиска, но можно использовать:
- 🔠 Левенштейн — расстояние между строками (количество изменений для превращения одной строки в другую).
- 🔤 N-граммы — разбиение строк на части и сравнение фрагментов.
- 📐 Внешние библиотеки (например, OneScript.Fuzzy для расширенных алгоритмов).
Пример реализации расстояния Левенштейна в 1С:
Функция РасстояниеЛевенштейна(Строка1, Строка2)
Длина1 = СтрДлина(Строка1);
Длина2 = СтрДлина(Строка2);
Матрица = Новый Массив(Длина1 + 1);
Для i = 0 По Длина1 Цикл
Матрица[i] = Новый Массив(Длина2 + 1);
Матрица[i][0] = i;
КонецЦикла;
Для j = 0 По Длина2 Цикл
Матрица[0][j] = j;
КонецЦикла;
Для i = 1 По Длина1 Цикл
Для j = 1 По Длина2 Цикл
Если Сред(Строка1, i, 1) = Сред(Строка2, j, 1) Тогда
Стоимость = 0;
Иначе
Стоимость = 1;
КонецЕсли;
Матрица[i][j] = Мин(
Матрица[i-1][j] + 1, // Удаление
Матрица[i][j-1] + 1, // Вставка
Матрица[i-1][j-1] + Стоимость // Замена
);
КонецЦикла;
КонецЦикла;
Возврат Матрица[Длина1][Длина2];
КонецФункции;
Применение для поиска похожих строк:
ПорогСходства = 2; // Максимальное расстояние Левенштейна
МассивСтрок = Новый Массив;
МассивСтрок.Добавить("ООО Ромашка");
МассивСтрок.Добавить("ООО Рамашка");
МассивСтрок.Добавить("ООО Ландыш");
Похожие = Новый Массив;
Для i = 0 По МассивСтрок.ВГраницах() - 1 Цикл
Для j = i + 1 По МассивСтрок.ВГраницах() Цикл
Расстояние = РасстояниеЛевенштейна(МассивСтрок[i], МассивСтрок[j]);
Если Расстояние <= ПорогСходства Тогда
Похожие.Добавить(СтрШаблон("'%1' похоже на '%2' (расстояние %3)",
МассивСтрок[i], МассивСтрок[j], Расстояние));
КонецЕсли;
КонецЦикла;
КонецЦикла;
⚠️ Внимание: Фаззи-поиск значительно замедляет обработку. Используйте его только для критических задач (например, очистки справочников) и ограничивайте глубину сравнения (например, первые 1000 элементов).
1. Предварительно нормализуйте строки (привести к нижнему регистру, убрать пробелы). 2. Сравнивайте только уникальные хэши строк (например, CRC32) для грубой фильтрации. 3. Используйте многопоточность (для 1С 8.3.20+ через Как ускорить фаззи-поиск?
ФоновоеЗадание).
6. Автоматизация проверки дублей в типовых конфигурациях
В типовых конфигурациях (УТ 11, БП 3.0, ЗУП 3.1) дубли часто возникают в справочниках (Номенклатура, Контрагенты, ФизическиеЛица). Чтобы избежать ручной проверки, можно:
- 🔧 Добавить обработку в события
ПередЗаписьюилиПриЗаписи. - 📅 Запускать регламентное задание для периодической очистки.
- 📊 Создать отчёт с выгрузкой дублей в Excel.
Пример обработки для справочника Контрагенты (проверка по ИНН):
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если НЕ ЗначениеЗаполнено(Объект.ИНН) Тогда
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Контрагенты.Ссылка КАК Ссылка
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.ИНН = &ИНН
| И Контрагенты.Ссылка <> &Ссылка";
Запрос.УстановитьПараметр("ИНН", Объект.ИНН);
Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);
Результат = Запрос.Выполнить();
Если Результат.Выбрать() Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Контрагент с таким ИНН уже существует: " + Результат.Ссылка.Наименование;
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры;
Для массовой проверки дублей в справочнике используйте этот запрос:
ВЫБРАТЬ
Контрагенты.ИНН КАК ИНН,
КОЛИЧЕСТВО(*) КАК Количество
ИЗ
Справочник.Контрагенты КАК Контрагенты
СГРУППИРОВАТЬ ПО
Контрагенты.ИНН
ИМЕЮЩИЕ
КОЛИЧЕСТВО(*) > 1
⚠️ Внимание: В типовых конфигурациях некоторые справочники (например, ФизическиеЛица в ЗУП) имеют встроенную проверку уникальности по полям. Перед добавлением своей логики проверьте настройки уникальности в конфигураторе (Справочники → [ИмяСправочника] → Уникальность).
В типовых конфигурациях сначала проверьте настройки уникальности справочников — возможно, дубли уже блокируются на уровне платформы.
FAQ: Частые вопросы по поиску дублей в 1С
Как найти дубли в массиве объектов по нескольким полям?
Используйте Структура с составным ключом. Например, для проверки дублей по Артикул + Размер:
Ключ = Элемент.Артикул + "|" + Элемент.Размер;
Если Уникальные.Свойство(Ключ) Тогда
// Нашли дубль
КонецЕсли;
Почему НайтиПовторяющиеся() не находит дубли в массиве справочников?
Метод сравнивает элементы по ссылке, а не по содержимому. Для справочников используйте проверку по уникальным реквизитам (например, Код или Наименование) через Структуру.
Как ускорить поиск дублей в массиве из 1 000 000 элементов?
Разбейте массив на части по 100 000 элементов и обрабатывайте их параллельно через ФоновыеЗадания (доступно в 1С 8.3.20+). Также поможет предварительная сортировка.
Можно ли найти дубли в табличной части документа?
Да, используйте ТаблицаЗначений.Свернуть() с группировкой по нужным колонкам:
ТЗ.Свернуть("Колонка1, Колонка2", "Количество");
Это покажет строки с одинаковыми значениями в указанных колонках.
Как выгрузить дубли в Excel для анализа?
Сформируйте ТаблицаЗначений с дублями и используйте ЗаписьXML или ПоместитьФайл:
ТаблицаДублей.Записать("C:\temp\дубли.xlsx", ТипФайлаExcel.XLSX);