В практике разработчика 1С часто возникает задача поиска повторяющихся данных в коллекциях, которые еще не записаны в базу данных. Типичный пример — обработка загруженных из внешнего файла артикулов или кодов, где дублями считаются строки, имеющие частичное совпадение (например, последние 5 символов). Рассмотрим подробно, какими инструментами располагает платформа 1С для решения этой задачи, проанализируем их эффективность и выберем оптимальные алгоритмы.
Один из самых быстрых способов поиска дублей в оперативной памяти — использование объекта Соответствие. Этот метод позволяет найти повторяющиеся элементы за один проход по таблице значений (сложность алгоритма O(n)). Суть метода заключается в том, что мы используем искомую часть строки в качестве ключа хеш-таблицы.
Разберем этот алгоритм по шагам:
Соответствие, где ключом будет подстрока, а значением — массив найденных строк таблицы.Прав).Посмотрим на пример кода, реализующий этот подход:
// Допустим, ТЗ_Данные - наша исходная таблица значений
НайденныеДубли = Новый Массив;
КэшЗначений = Новый Соответствие;
Для Каждого СтрокаТЗ Из ТЗ_Данные Цикл
// Выделяем 5 символов справа для сравнения
КлючПоиска = Прав(СокрЛП(СтрокаТЗ.Номер), 5);
// Ищем, встречался ли нам такой ключ ранее
СуществующаяЗапись = КэшЗначений.Получить(КлючПоиска);
Если СуществующаяЗапись = Неопределено Тогда
// Это первое вхождение, запоминаем строку
КэшЗначений.Вставить(КлючПоиска, СтрокаТЗ);
Иначе
// Мы нашли дубль!
// Добавляем текущую строку в список дублей
НайденныеДубли.Добавить(СтрокаТЗ);
// Если нужно собрать ВСЕ строки (включая самую первую),
// можно проверять тип значения или хранить в соответствии массив
Если ТипЗнч(СуществующаяЗапись) <> Тип("Массив") Тогда
НайденныеДубли.Добавить(СуществующаяЗапись);
// Помечаем, что оригинал уже добавлен в результат
КэшЗначений.Вставить(КлючПоиска, Новый Массив);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Важный нюанс: Почему мы используем Соответствие, а не Структура? Объект Структура в 1С имеет ограничение на именование ключей (они должны соответствовать правилам идентификаторов), в то время как Соответствие позволяет использовать в качестве ключа любую строку, включая те, что начинаются с цифр или содержат спецсимволы.
Этот способ часто вызывает споры среди разработчиков из-за нагрузки на сервер СУБД, однако он остается одним из самых простых в реализации. Проанализируем ситуацию: если таблица значений уже находится на сервере, выгрузка её в менеджер временных таблиц происходит достаточно быстро.
Алгоритм действий следующий:
Прав, так как в самом языке запросов 1С функция ПРАВО (RIGHT) может не поддерживаться для некоторых типов полей (что часто требует сравнения строки с представлением ссылки) или СУБД внутри временных таблиц (в зависимости от версии платформы).ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1.Пример реализации запросом (для отладки таких алгоритмов удобно использовать консоль запросов для управляемых форм):
// Подготовим ТЗ, добавив колонку для поиска
ТЗ_Данные.Колонки.Добавить("КлючДублей", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(5)));
Для Каждого Стр Из ТЗ_Данные Цикл
Стр.КлючДублей = Прав(СокрЛП(Стр.Номер), 5);
КонецЦикла;
Запрос = Новый Запрос;
Запрос.МенеджерВременных Таблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ;
|ВЫБРАТЬ КлючДублей ИЗ ВТ_Данные
|СГРУППИРОВАТЬ ПО КлючДублей
|ИМЕЮЩИЕ КОЛИЧЕСТВО(КлючДублей) > 1";
Запрос.УстановитьПараметр("ТЗ", ТЗ_Данные);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
// В РезультатЗапроса теперь список ключей, которые дублируются
Рассмотрим преимущества этого метода: он идеален, когда после поиска дублей данные нужно соединить с таблицами из базы данных (например, чтобы проверить, не заняты ли уже эти номера в справочнике Номенклатура).
Если мы не хотим использовать дополнительные объекты (соответствия) или запросы, можно воспользоваться классическим алгоритмом сортировки. Выясним причину его популярности: он нагляден и потребляет минимум памяти при больших объемах данных.
Проанализируем шаги реализации:
ТЗ.Сортировать("ИмяКолонки").Посмотрим на пример кода с корректной обработкой "первого" дубля:
// 1. Подготовка данных
ТЗ_Данные.Колонки.Добавить("Суффикс", Новый ОписаниеТипов("Строка"));
Для Каждого Стр Из ТЗ_Данные Цикл
Стр.Суффикс = Прав(Стр.Номер, 5);
КонецЦикла;
// 2. Сортировка
ТЗ_Данные.Сортировать("Суффикс");
// 3. Поиск
ПредыдущаяСтрока = Неопределено;
СписокДублей = Новый Массив;
Для Счетчик = 0 По ТЗ_Данные.Количество() - 1 Цикл
ТекущаяСтрока = ТЗ_Данные[Счетчик];
Если ПредыдущаяСтрока <> Неопределено Тогда
Если ТекущаяСтрока.Суффикс = ПредыдущаяСтрока.Суффикс Тогда
// Нашли совпадение!
СписокДублей.Добавить(ТекущаяСтрока);
// Важно добавить и первую строку из серии дублей, если она еще не там
Если СписокДублей.Найти(ПредыдущаяСтрока) = Неопределено Тогда
СписокДублей.Добавить(ПредыдущаяСтрока);
КонецЕсли;
КонецЕсли;
КонецЕсли;
ПредыдущаяСтрока = ТекущаяСтрока;
КонецЦикла;
Платформа 1С предоставляет встроенный метод Свернуть, который может быть очень эффективен для быстрого получения списка уникальных "ключей" и их количества. Разберем этот вариант подробнее.
Алгоритм действий:
Количество и заполняем её единицей для каждой строки.Количество.Количество = 1.
ТЗ_Копия = ТЗ_Данные.Скопировать();
ТЗ_Копия.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТЗ_Копия.ЗаполнитьЗначения(1, "Количество");
// Предполагаем, что колонка "Суффикс" уже заполнена
ТЗ_Копия.Свернуть("Суффикс", "Количество");
// Теперь в ТЗ_Копия только уникальные суффиксы и их количество
// Удалим те, что не являются дублями
Индекс = ТЗ_Копия.Количество() - 1;
Пока Индекс >= 0 Цикл
Если ТЗ_Копия[Индекс].Количество == 1 Тогда
ТЗ_Копия.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Рассмотрим технические особенности каждого подхода, чтобы понимать, когда какой метод использовать:
ТЗ.Индексы.Добавить("ИмяКолонки"). Это значительно ускорит методы Найти и НайтиСтроки.Практический совет: При работе со строками внутри циклов (функция Прав) помните, что на огромных массивах данных (миллионы строк) строковые операции могут стать "узким местом". В таких случаях рекомендуется один раз вычислить ключи, записать их в отдельную колонку и далее работать только с ней. Также использование объекта СравнениеЗначений может быть полезно, если в колонке могут встретиться разные типы данных, но для простых строк стандартные операторы сравнения работают быстрее.
Подводя итог, для разовой задачи по поиску дублей в небольшой таблице значений (до 10-20 тысяч строк) наиболее простым и эффективным будет использование Соответствия. Это позволит избежать лишних обращений к СУБД и обеспечит высокую скорость выполнения кода.