Работа с массивами в 1С:Предприятие — одна из самых частых задач, с которыми сталкиваются разработчики. Но как только в массиве появляются повторяющиеся элементы, даже простая обработка данных превращается в головную боль. Дубликаты искажают результаты отчетов, замедляют выполнение алгоритмов и могут приводить к логическим ошибкам в бизнес-процессах. Например, при формировании прайс-листа с уникальными товарами или при анализе клиентской базы без повторяющихся контактов.

В этой статье мы разберем 7 проверенных способов удаления дублей — от стандартных методов платформы до оптимизированных алгоритмов для больших массивов. Вы узнаете, какой подход выбрать для Массива, СпискаЗначений или ТаблицыЗначений, как избежать типичных ошибок при работе с составными типами данных, и почему иногда лучше использовать внешние компоненты вместо встроенных функций. Все примеры приведены с учетом особенностей 1С 8.3 (включая последние обновления) и протестированы на реальных конфигурациях.

1. Стандартный метод Платформы: УникальныеЗначения()

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

Пример использования для массива строк:

МассивСтрок = Новый Массив;

МассивСтрок.Добавить("Яблоко");

МассивСтрок.Добавить("Банан");

МассивСтрок.Добавить("Яблоко"); // Дубликат

МассивСтрок.Добавить("Апельсин");

УникальныйМассив = МассивСтрок.УникальныеЗначения();

// Результат: ["Яблоко", "Банан", "Апельсин"]

Преимущества метода:

  • 🔹 Минимальный код — одна строка вместо циклов и проверок.
  • 🔹 Сохранение порядка — первый встреченный элемент остается в результате.
  • 🔹 Работает с любыми типами — строки, числа, даты, ссылки на справочники.

Однако у этого подхода есть ограничения:

  • ⚠️ Низкая производительность на больших массивах (10 000+ элементов).
  • ⚠️ Не работает с составными типами (например, массивы структур или объектов).
💡

Если вам нужно удалить дубли в ТаблицеЗначений, используйте метод УникальныеСтроки() с указанием колонок для сравнения: Таблица.УникальныеСтроки("Наименование, Артикул").

2. Ручное удаление дублей через цикл (для сложных типов)

Когда стандартные методы не подходят — например, при работе с массивами структур или объектов — приходится писать кастомный алгоритм. Основная идея: создать новый массив и добавлять в него только уникальные элементы, проверяя их наличие через цикл.

Пример для массива структур с проверкой по нескольким полям:

МассивСтруктур = Новый Массив;

МассивСтруктур.Добавить(Новый Структура("Код,Наименование", 1, "Товар1"));

МассивСтруктур.Добавить(Новый Структура("Код,Наименование", 2, "Товар2"));

МассивСтруктур.Добавить(Новый Структура("Код,Наименование", 1, "Товар1")); // Дубликат

УникальныйМассив = Новый Массив;

Для Каждого Элемент Из МассивСтруктур Цикл

Найден = Ложь;

Для Каждого УникальныйЭлемент Из УникальныйМассив Цикл

Если УникальныйЭлемент.Код = Элемент.Код И УникальныйЭлемент.Наименование = Элемент.Наименование Тогда

Найден = Истина;

Прервать;

КонецЕсли;

КонецЦикла;

Если Не Найден Тогда

УникальныйМассив.Добавить(Элемент);

КонецЕсли;

КонецЦикла;

Этот метод универсален, но имеет квадратичную сложность O(n²) — при 1000 элементов потребуется около миллиона сравнений. Для оптимизации можно использовать Соответствие (хэш-таблицу), как показано в следующем разделе.

📊 Какой тип массивов вы чаще обрабатываете в 1С?
Простые (строки, числа)
Структуры
Объекты (справочники, документы)
Таблицы значений

3. Оптимизированный алгоритм с использованием Соответствия

Для ускорения проверки на уникальность вместо вложенных циклов можно задействовать объект Соответствие. Он работает как хэш-таблица: ключом служит уникальный идентификатор элемента (например, строка или число), а значением — сам элемент. Это снижает сложность алгоритма до O(n).

Пример для массива чисел:

МассивЧисел = Новый Массив;

МассивЧисел.Добавить(10);

МассивЧисел.Добавить(20);

МассивЧисел.Добавить(10); // Дубликат

Соответствие = Новый Соответствие;

УникальныйМассив = Новый Массив;

Для Каждого Число Из МассивЧисел Цикл

Если Не Соответствие.СодержитКлюч(Число) Тогда

Соответствие.Вставить(Число, Истина);

УникальныйМассив.Добавить(Число);

КонецЕсли;

КонецЦикла;

Для сложных типов (структур, объектов) в качестве ключа можно использовать:

  • 🔑 Строковое представлениеСтрока(Структура) (не всегда надежно).
  • 🔑 Хэш-суммуХэшДанных(Строка(Структура)).
  • 🔑 Уникальный идентификатор — например, Справочник.УникальныйИдентификатор().
Что делать, если ключ слишком длинный для Соответствия?

В 1С ограничение на длину ключа в Соответствии — 250 символов. Если ваше строковое представление объекта длиннее, используйте хэш-функцию:

Хэш = ХэшДанных(Строка(Объект), "MD5");

Соответствие.Вставить(Хэш, Объект);

Предупреждение: при работе с плавающими числами (типа Число(10,5)) или датами с временем возможны ошибки из-за неточного сравнения. Например, 1.0000001 и 1.0000002 могут считаться разными ключами. В таких случаях лучше округлять значения перед добавлением в Соответствие.

4. Удаление дублей в ТаблицеЗначений с группировкой

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

Пример: удаление дублей по колонкам "Контрагент" и "Договор":

Таблица = Новый ТаблицаЗначений;

Таблица.Колонки.Добавить("Контрагент");

Таблица.Колонки.Добавить("Договор");

Таблица.Колонки.Добавить("Сумма");

// Заполняем таблицу (включая дубли)

...

// Группируем по уникальным комбинациям Контрагент+Договор

Группировка = Таблица.Сгруппировать("Контрагент,Договор");

Группировка.Итоги.Добавить("Сумма", ТипИтога.Сумма);

// Результат в Группировка.Выгрузить()

Особенности метода:

  • 📊 Сохраняет агрегатные функции (сумма, количество, максимум).
  • 🔄 Позволяет гибко настраивать группировку по нескольким колонкам.
  • ⚠️ Требует указания колонок для сравнения — иначе дубли не будут удалены.

Убедитесь, что колонки для группировки имеют одинаковый тип данных|Проверьте отсутствие NULL-значений в ключевых колонках|Настройте итоги для числовых колонок (если нужны)|Протестируйте результат на небольшом фрагменте данных-->

5. Использование запроса для удаления дублей

Для больших объемов данных (десятки тысяч строк) оптимальным решением станет запрос на языке 1С. Он выполняется на стороне СУБД и обрабатывает данные гораздо быстрее, чем скрипты на встроенном языке. Особенно актуально для конфигураций на управляемых формах или при работе с регламентными заданиями.

Пример запроса для выборки уникальных строк из виртуальной таблицы:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ РАЗЛИЧНЫЕ

| Товар,

| Характеристика,

| Цена

|ИЗ

| Документ.ПоступлениеТоваров.Товары

|ГДЕ

| Ссылка = &СсылкаНаДокумент";

Запрос.УстановитьПараметр("СсылкаНаДокумент", Ссылка);

Результат = Запрос.Выполнить().Выгрузить();

Ключевые преимущества:

Критерий Запрос Скрипт на встроенном языке
Скорость обработки ⚡ Очень высокая (оптимизировано СУБД) 🐢 Низкая (поэлементная обработка)
Память 🟢 Минимальные затраты 🔴 Высокие (хранение промежуточных массивов)
Гибкость 🟡 Ограничена синтаксисом запроса 🟢 Полный контроль над логикой

Обратите внимание: оператор РАЗЛИЧНЫЕ удаляет дубликаты только по всем выбранным полям. Если нужно оставить уникальность по части колонок, используйте подзапросы или ГРУППИРОВКА ПО.

💡

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

6. Работа с дублями в массивах ссылок на объекты

Особую сложность представляют массивы, содержащие ссылки на объекты 1С (справочники, документы, планы видов характеристик). Стандартные методы вроде УникальныеЗначения() здесь не работают, так как сравнение ссылок происходит по их уникальным идентификаторам, а не по содержимому.

Пример: удаление дублей ссылок на номенклатуру с учетом дополнительных реквизитов:

МассивНоменклатуры = Новый Массив;

МассивНоменклатуры.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Товар1"));

МассивНоменклатуры.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Товар1")); // Дубликат

// Используем Соответствие с ключом по УникальномуИдентификатору()

Соответствие = Новый Соответствие;

УникальныйМассив = Новый Массив;

Для Каждого Ссылка Из МассивНоменклатуры Цикл

Если Не Соответствие.СодержитКлюч(Ссылка.УникальныйИдентификатор()) Тогда

Соответствие.Вставить(Ссылка.УникальныйИдентификатор(), Истина);

УникальныйМассив.Добавить(Ссылка);

КонецЕсли;

КонецЦикла;

Если нужно учитывать не только саму ссылку, но и связанные данные (например, количество или цену), используйте составной ключ:

Ключ = Ссылка.УникальныйИдентификатор() + "|" + Строка(Количество) + "|" + Строка(Цена);

Соответствие.Вставить(Ключ, Истина);

💡

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

7. Внешние компоненты и расширения для сложных случаев

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

  • 🛠️ OneScript — для написания высокопроизводительных скриптов на C#.
  • 🛠️ AddIn "FastArray" — оптимизированные алгоритмы для массивов.
  • 🛠️ 1C:Enterprise Development Tools (EDT) — для интеграции с Java-библиотеками.

Пример использования OneScript для нечеткого сравнения строк (удаление "похожих" дублей):

// Подключаем внешнюю компоненту

ВнешняяКомпонента = Новый COMОбъект("OneScript.StandardLibrary");

// Функция сравнения по алгоритму Левенштейна

Функция ПохожиеСтроки(Строка1, Строка2, Порог = 0.8) Экспорт

Расстояние = ВнешняяКомпонента.LevenshteinDistance(Строка1, Строка2);

МаксДлина = Макс(СтрДлина(Строка1), СтрДлина(Строка2));

Возврат (1 - Расстояние / МаксДлина) >= Порог;

КонецФункции

Предупреждения при работе с внешними компонентами:

⚠️ Внимание: Внешние компоненты могут конфликтовать с обновлениями платформы 1С. Всегда тестируйте их на копии рабочей базы перед внедрением в продакшн.
⚠️ Внимание: Нечеткое сравнение строк увеличивает нагрузку на процессор. Используйте его только для критичных задач (например, очистка справочника контрагентов от опечаток).

FAQ: Частые вопросы по удалению дублей в 1С

Как удалить дубли в массиве, если элементы — это объекты с вложенными коллекциями?

Для таких случаев нужно реализовать кастомный компаратор. Создайте функцию, которая рекурсивно обходит все свойства объекта и формирует уникальный ключ (например, JSON-строку или хэш). Затем используйте этот ключ для проверки через Соответствие.

Пример:

Функция ПолучитьКлючОбъекта(Объект)

Возврат ХэшДанных(ЗаписатьJSON(Объект));

КонецФункции

Почему после удаления дублей в ТаблицеЗначений остаются пустые строки?

Это происходит, если в колонках, по которым выполняется группировка, есть NULL-значения. Перед удалением дублей выполните очистку:

Таблица.Колонки.Наименование.ЗаполнитьЗначения(""); // Замена NULL на пустую строку
Как удалить дубли в массиве, сохраняя только последнее вхождение?

Стандартные методы оставляют первое вхождение. Чтобы инвертировать логику, сначала разверните массив (Массив.Развернуть()), затем примените УникальныеЗначения(), и снова разверните результат.

Можно ли удалить дубли в массиве без создания нового массива?

Нет, в 1С все методы удаления дублей (УникальныеЗначения(), группировка) возвращают новый объект. Если критично экономить память, удаляйте элементы "на месте" через цикл с обратным порядком:

Для i = Массив.ВГраница() По 1 Шаг -1 Цикл

Если Массив[i] = Массив[i-1] Тогда

Массив.Удалить(i);

КонецЕсли;

КонецЦикла;

Но этот метод работает только для отсортированных массивов с подряд идущими дублями.

Как удалить дубли в массиве с учетом регистра символов?

По умолчанию сравнение строк в 1С регистронезависимое. Чтобы учитывать регистр, преобразуйте строки к единому виду перед сравнением:

Соответствие.Вставить(Строка(Элемент).ВРегистре("U"), Элемент); // Приведение к верхнему регистру