Как эффективно удалить дубли строк в таблице значений или массиве 1С, оставив только первый экземпляр

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

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

Метод 1. Использование объекта Соответствие (Map)

Проанализируем самый быстрый программный способ поиска дублей без использования запросов. Объект Соответствие в 1С работает по принципу хеш-таблицы, что обеспечивает практически мгновенный поиск ключа даже в коллекциях из десятков тысяч элементов. Этот метод идеально подходит, если нам нужно оставить первую встреченную строку, а остальные удалить.

Разберем алгоритм: мы будем обходить исходную таблицу, проверять наличие ключевого значения в Соответствии. Если значения еще нет — добавляем его туда. Если оно уже есть — помечаем текущую строку на удаление.

Важный нюанс: Чтобы не нарушить индексы при удалении строк "на лету", мы сначала соберем все ненужные строки в отдельный массив, а затем удалим их из таблицы.


// Предположим, ТЗ - наша исходная таблица значений
// КлючУникальности - имя колонки, по которой ищем дубли

СоответствиеКлючей = Новый Соответствие;
МассивНаУдаление = Новый Массив;

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

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

Этот способ оптимален по памяти и скорости, так как мы совершаем всего два прохода по данным.

Метод 2. Оптимизированный запрос с использованием Временных Таблиц

Если объем данных в ТаблицеЗначений превышает несколько десятков тысяч строк, программный перебор может замедлить работу системы. В таких случаях мы рекомендуем использовать механизм запросов. Это позволяет переложить задачу поиска дублей на СУБД, которая справится с этим значительно быстрее. Для более сложных сценариев пакетной обработки данных пригодится статья об особенностях работы с временными таблицами в 1С.

Рассмотрим по шагам, как это реализовать:

  1. Передаем ТаблицуЗначений в запрос в качестве параметра.
  2. В тексте запроса используем конструкцию СГРУППИРОВАТЬ ПО или ВЫБРАТЬ РАЗЛИЧНЫЕ.
  3. Выгружаем результат обратно в таблицу.

Посмотрим на пример кода, где нам нужно оставить только уникальные характеристики:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВТ_Данные.Характеристика КАК Характеристика,
    |   МАКСИМУМ(ВТ_Данные.Цена) КАК Цена,
    |   МАКСИМУМ(ВТ_Данные.Количество) КАК Количество
    |ПОМЕСТИТЬ ВТ_Уникальные
    |ИЗ
    |   &ВТ_Исходная КАК ВТ_Данные
    |
    |СГРУППИРОВАТЬ ПО
    |   ВТ_Данные.Характеристика;
    |
    |ВЫБРАТЬ * ИЗ ВТ_Уникальные";

// Важно: колонки в ТЗ должны иметь типизацию
Запрос.УстановитьПараметр("ВТ_Исходная", ТЗ);
ТЗ = Запрос.Выполнить().Выгрузить();

Обратите внимание: Если в других колонках (например, Цена) данные различаются, использование агрегатных функций типа МАКСИМУМ или МИНИМУМ позволит вам предсказать, какие именно данные попадут в итоговую строку. Если же вам нужно просто "первую попавшуюся" строку из исходной ТЗ, в запросе можно добавить колонку НомерСтроки и группировать по ней (выбирая минимальный номер).

Метод 3. Встроенный метод Свернуть()

Это самый быстрый в написании способ, предоставляемый платформой 1С. Метод Свернуть объединяет строки с одинаковыми значениями в указанных колонках группировки.

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

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


// Группируем по Характеристике, суммируем Количество
// Все остальные колонки, не указанные здесь, будут удалены!
ТЗ.Свернуть("Характеристика", "Количество");

Используйте этот метод только тогда, когда вам достаточно оставить только ключевые колонки и числовые показатели.

Метод 4. Обратный цикл удаления (без промежуточного массива)

Если вы принципиально хотите удалять строки прямо в основном цикле, проанализируем ситуацию с индексами. При удалении строки с индексом 5, строка, которая была 6-й, становится 5-й. Обычный цикл Для Каждого или цикл по возрастанию индекса пропустит эту строку. Поэтому мы используем обход с конца.


КоличествоСтрок = ТЗ.Количество();
Для Счетчик = 1 По КоличествоСтрок Цикл
    Индекс = КоличествоСтрок - Счетчик;
    ТекущаяСтрока = ТЗ[Индекс];
    
    // Логика проверки дубля (например, через НайтиСтроки)
    СтруктураПоиска = Новый Структура("Характеристика", ТекущаяСтрока.Характеристика);
    Найденные = ТЗ.НайтиСтроки(СтруктураПоиска);
    
    Если Найденные.Количество() > 1 Тогда
        // Если нашли больше одной строки, проверяем, не является ли текущая "первой"
        // Чтобы оставить первую, удаляем только если индекс больше индекса первой найденной
        Если ТЗ.Индекс(Найденные[0]) < Индекс Тогда
            ТЗ.Удалить(ТекущаяСтрока);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Этот метод считается менее производительным из-за постоянного вызова НайтиСтроки внутри цикла, но он наглядно демонстрирует работу с индексами коллекции.

Практические советы и нюансы

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

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

← На главную