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