Как выбрать уникальные записи из массива структур в 1С 8.3

Программист 1С v8.3 (Управляемые формы)
← На главную

В процессе разработки на платформе 1С:Предприятие 8 часто возникает задача обработки коллекций данных, полученных из различных источников: внешних API, файлов или сложных запросов. Одной из типичных проблем является необходимость отфильтровать дублирующиеся записи в массиве структур, например, когда из полного списка товаров нужно выделить только уникальные родительские категории с их кодами.

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

Способ 1: Использование Таблицы Значений и метода Свернуть

Это самый популярный и интуитивно понятный способ для разработчиков 1С. Если у вас уже есть массив структур, мы можем быстро преобразовать его в ТаблицаЗначений, которая обладает мощным встроенным методом Свернуть().

Рассмотрим алгоритм реализации этого метода:

  1. Создаем пустую таблицу значений.
  2. Добавляем в нее колонки, соответствующие полям структуры, по которым нужно определить уникальность.
  3. Заполняем таблицу данными из массива.
  4. Вызываем метод Свернуть(), указывая колонки группировки.

Проанализируем пример кода:


// Предположим, МассивДанных уже заполнен структурами
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("РодительскаяКатегорияКод");
ТЗ.Колонки.Добавить("РодительскаяКатегория");

// Переносим данные из массива в ТЗ
Для Каждого Элемент Из МассивДанных Цикл
    НоваяСтрока = ТЗ.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Элемент);
КонецЦикла;

// Сворачиваем таблицу по двум колонкам
// После выполнения в ТЗ останутся только уникальные пары Код + Наименование
ТЗ.Свернуть("РодительскаяКатегорияКод, РодительскаяКатегория");

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

Способ 2: Использование языка запросов (ВЫБРАТЬ РАЗЛИЧНЫЕ)

Если данные изначально выбираются из базы данных, как в примере из сообщения 4, то эффективнее всего отсечь дубли еще на этапе выполнения запроса. Для этого используется ключевое слово РАЗЛИЧНЫЕ.

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

Посмотрим, как это реализовать:


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ РАЗЛИЧНЫЕ
|    ВТ_Данные.РодительскаяКатегорияКод КАК Код,
|    ВТ_Данные.РодительскаяКатегория КАК Наименование
|ИЗ
|    &ТаблицаДанных КАК ВТ_Данные";

// В качестве параметра передаем ТЗ, сформированную из массива
Запрос.УстановитьПараметр("ТаблицаДанных", ТЗ); 
Результат = Запрос.Выполнить().Выгрузить();

Этот подход особенно удобен, если помимо уникальности вам нужно выполнить дополнительную сортировку или фильтрацию данных средствами SQL.

Способ 3: Оптимизированный алгоритм через Соответствие (Map)

При работе с очень большими массивами (десятки и сотни тысяч записей) методы с ТаблицаЗначений могут работать медленнее из-за накладных расходов на создание объекта. В таких случаях программисты используют объект Соответствие.

Основное преимущество Соответствие заключается в том, что поиск по ключу в нем реализован через хеш-таблицы, что обеспечивает практически мгновенный доступ (сложность O(1)). Мы можем использовать составной ключ для обеспечения уникальности по нескольким полям.

Разберем пример реализации:


УникальныеЗаписи = Новый Соответствие;
Для Каждого Запись Из МассивДанных Цикл
    // Формируем уникальный ключ из кода и наименования
    Ключ = Строка(Запись.РодительскаяКатегорияКод) + "___" + Запись.РодительскаяКатегория;
    
    // Если ключ уже есть, значение просто перезапишется, дубли не возникнут
    УникальныеЗаписи.Вставить(Ключ, Запись);
КонецЦикла;

// Теперь из соответствия можно получить чистый массив уникальных структур
ИтоговыйМассив = Новый Массив;
Для Каждого КлючИЗначение Из УникальныеЗаписи Цикл
    ИтоговыйМассив.Добавить(КлючИЗначение.Значение);
КонецЦикла;

Этот метод считается наиболее "профессиональным" с точки зрения алгоритмической сложности. Он работает за линейное время O(n), в отличие от вложенных циклов перебора массива, которые имеют квадратичную сложность O(n²).

Ошибки при реализации и советы по оптимизации

Проанализируем типичные ошибки, которые допускают разработчики при решении данной задачи:

  1. Проблема N+1 запроса: В сообщении 4 автор вызывает функции ВернутьРодителя(Товар) внутри цикла. Это крайне неэффективно, так как при наличии 1000 товаров будет выполнено 1000 дополнительных запросов к базе. Правильное решение: выгружать родителя и его код сразу в основном запросе через точку (например, Товар.Родитель.Наименование).
  2. Типизация данных: При сравнении уникальности 1С учитывает тип. Если в одном случае код — это Число, а в другом — Строка, система посчитает их разными. Рекомендуется принудительно приводить ключевые поля к строке с помощью функции Строка() или СокращенноЛП().
  3. Регистр строк: Помните, что поиск в Соответствие чувствителен к регистру. Если названия категорий могут отличаться регистром (например, "Обувь" и "обувь"), перед вставкой в ключ используйте функцию ВРег().

Использование Библиотеки Стандартных Подсистем (БСП)

Если ваша конфигурация построена на базе современных типовых решений (УТ 11, ERP, БП 3.0), в ней наверняка присутствует БСП. Перед тем как писать свой велосипед, проверьте наличие готовых инструментов в общем модуле ОбщегоНазначения.

Функция ОбщегоНазначения.УдалитьПовторяющиесяЭлементыМассива(Массив) позволяет быстро очистить массив от дублей, если элементами являются простые типы или ссылки. Однако для массива структур она не подойдет напрямую, так как структуры — это ссылочные объекты, и две разные структуры с одинаковыми полями не считаются идентичными для этой функции.

Для работы со структурами в БСП лучше использовать комбинацию функций конвертации ТаблицаЗначенийВМассив и МассивВТаблицуЗначений, чтобы применить метод Свернуть, не занимаясь ручным перебором строк.

Выбор конкретного метода зависит от ваших условий. Для небольших коллекций и удобства поддержки кода идеально подходит Таблица Значений. Для высоконагруженных систем и огромных массивов выбирайте Соответствие. А если данные все равно нужно получать из БД — используйте Запрос с ключевым словом РАЗЛИЧНЫЕ.

← На главную