При разработке на платформе 1С:Предприятие программисты часто сталкиваются с ситуацией, когда в массиве скапливаются одинаковые значения (ссылки, строки или числа), а для дальнейшей логики программы требуется получить только уникальные записи. Рассмотрим подробнее, как решить эту задачу различными способами, проанализируем их производительность и выберем оптимальный вариант для каждой конкретной ситуации.
Если ваша конфигурация разработана на основе БСП, то наиболее простым и правильным решением будет использование уже готовых процедур общего назначения. В этом вам может пригодиться справочник по методам БСП с примерами использования. Проанализируем возможности модуля ОбщегоНазначенияКлиентСервер. В нем реализованы методы, которые позволяют манипулировать массивами без написания лишнего кода.
Разберем пример использования метода СвернутьМассив():
// Передаем массив, который содержит дубли
ОбщегоНазначенияКлиентСервер.СвернутьМассив(МойМассив);
// Теперь МойМассив содержит только уникальные значения
Также существует альтернативная функция ДополнитьМассив(), которая позволяет не только объединять коллекции, но и сразу отсекать дубликаты. Посмотрим на пример кода:
// МассивПриемник — куда добавляем, МассивИсточник — откуда берем
// Третий параметр Истина указывает на то, что нужно добавлять только уникальные значения
ОбщегоНазначенияКлиентСервер.ДополнитьМассив(МассивПриемник, МассивИсточник, Истина);
Важный момент: Эти методы являются «обертками» над стандартными алгоритмами платформы. Использование подобных оберток — хороший тон. Если в вашей конфигурации нет БСП (она написана «с нуля»), данные функции будут недоступны, и нам придется использовать другие механизмы.
Проанализируем работу объекта Соответствие. Это один из самых быстрых способов получения уникальных значений благодаря особенностям хранения данных в памяти. Ключи в объекте Соответствие (Map) всегда уникальны. Если мы попытаемся добавить в него значение с уже существующим ключом, старое значение просто заменится новым, но сам ключ останется в единственном экземпляре.
Выясним, как реализовать этот алгоритм на практике:
// Создаем временный объект соответствие
УникальныеДанные = Новый Соответствие;
Для Каждого Элемент Из ИсходныйМассив Цикл
// Используем элемент массива в качестве ключа
УникальныеДанные.Вставить(Элемент, Истина);
КонецЦикла;
// Выгружаем уникальные ключи обратно в массив
НовыйМассив = Новый Массив;
Для Каждого КлючИЗначение Из УникальныеДанные Цикл
НовыйМассив.Добавить(КлючИЗначение.Ключ);
КонецЦикла;
Этот метод работает со скоростью O(n), что делает его крайне эффективным для больших массивов (сотни тысяч элементов), так как поиск в Соответствии происходит практически мгновенно.
Рассмотрим ситуацию, когда массив нужно обработать на стороне сервера. В 1С объект ТаблицаЗначений обладает мощным встроенным методом Свернуть(). При этом стоит учитывать, что производительность способов обхода таблицы значений может варьироваться в зависимости от структуры данных. Разберем по шагам, как это работает:
ТаблицаЗначений.Свернуть() по имени колонки.Пример кода реализации:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Значение");
// Загружаем массив в колонку ТЗ
ТЗ.ЗагрузитьКолонку(ИсходныйМассив, "Значение");
// Сворачиваем таблицу по колонке, удаляя дубли
ТЗ.Свернуть("Значение");
// Получаем массив уникальных значений
УникальныйМассив = ТЗ.ВыгрузитьКолонку("Значение");
Преимущество: Этот метод очень удобен, если у вас в массиве лежат сложные данные или если вам нужно выполнить группировку по нескольким полям сразу. Однако создание объекта ТаблицаЗначений требует больше ресурсов памяти, чем работа с Соответствием.
Посмотрим на ситуацию, когда массив содержит огромное количество ссылочных данных. В 1С можно использовать объект Запрос, даже если данные еще не записаны в базу данных — для написания и отладки таких алгоритмов пригодится универсальная консоль запросов и кода для разработчиков. Мы можем передать массив как параметр и использовать временные таблицы.
Проанализируем пример кода:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ВременнаяТаблица.Значение КАК Значение
|ПОМЕСТИТЬ ВТ_Уникальные
|ИЗ
| &ИсходныйМассив КАК ВременнаяТаблица
|;
|
|ВЫБРАТЬ
| ВТ_Уникальные.Значение
|ИЗ
| ВТ_Уникальные";
Запрос.УстановитьПараметр("ИсходныйМассив", ИсходныйМассив);
РезультатЗапроса = Запрос.Выполнить();
УникальныйМассив = РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Значение");
Этот способ позволяет переложить нагрузку по обработке данных на СУБД, что может быть оправдано при очень больших объемах данных и сложных типах.
Иногда разработчики пытаются удалять дубли напрямую в цикле по массиву. Разберем, почему код Массив.Удалить(Массив.Найти(ТекущееЗначение)) может работать некорректно. При удалении элемента из середины массива индексы всех последующих элементов сдвигаются. Если вы используете цикл Для Счетчик = 0 По Массив.Количество() - 1, вы рискуете либо пропустить элементы, либо получить ошибку выхода за границы массива. Для поиска таких и других уязвимых мест в коде полезно проводить анализ конфигураций на наличие ошибок или использовать статический анализатор кода проектов.
Совет: Если вы все же решили удалять элементы вручную, всегда делайте обход массива в обратном порядке (от конца к началу):
Для Индекс = -(Массив.Количество() - 1) По 0 Цикл
ТекущийИндекс = -Индекс;
// Логика проверки и удаления
КонецЦикла;
Подведем итог нашему анализу:
ОбщегоНазначенияКлиентСервер.СвернутьМассив(). Это стандарт де-факто в современной разработке на 1С.Соответствие.ТаблицуЗначений.Запрос с конструкцией ВЫБРАТЬ РАЗЛИЧНЫЕ.Выяснив причину появления дублей и выбрав подходящий инструмент, вы сможете сделать ваш код более производительным и надежным — для удаления дубликатов непосредственно из справочников и документов базы данных есть обработка поиска и устранения дублей в 1С.