К статье
Практический материал

Разработка и Конфигурация · 10 мин

Как удалить одинаковые строки в таблице значений 1С

Способы удалить дубли строк в таблице значений 1С: Свернуть(), поиск дублей по колонкам, удаление через индексы. Примеры кода, ошибки и проверка результата.

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

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

Ниже разобраны рабочие варианты кода, условия их применения, типичные ошибки и способы проверить, что очистка прошла корректно и не удалила лишнего.

Когда строка считается дублем: определяем критерий

Прежде чем писать код, нужно ответить на вопрос: какие строки считать одинаковыми. Возможны две ситуации. Первая — полное совпадение: все значения во всех колонках двух строк равны. Вторая — частичное совпадение: строки считаются дублями, если совпадают значения только в ключевых колонках, например «Номенклатура» и «Склад», а количество или цена могут отличаться.

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

Быстрое удаление полных дублей методом Свернуть()

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

// ТЗ — исходная таблица значений

СписокКолонок = "";

Для Каждого Колонка Из ТЗ.Колонки Цикл

СписокКолонок = СписокКолонок + ?(СписокКолонок = "", "", ",") + Колонка.Имя;

КонецЦикла;

ТЗ.Свернуть(СписокКолонок);

Важная особенность: метод работает «на месте», то есть изменяет саму таблицу, а не возвращает новую. Если исходные данные ещё понадобятся, сначала сделайте копию через ТЗ.Скопировать().

Удаление дублей по части колонок с суммированием

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

// Дубль = одинаковые Номенклатура и Склад,

// количество и сумма складываются

ТЗ.Свернуть("Номенклатура,Склад", "Количество,Сумма");

После такого вызова в таблице останутся только четыре перечисленные колонки. Все прочие колонки (комментарии, даты, ссылки на документы) будут удалены. Если они нужны, их придётся присоединить обратно отдельным запросом или перенести логику свёртки в запрос с конструкцией СГРУППИРОВАТЬ ПО.

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

Удаление дублей перебором без агрегации

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

Отбор = Новый Структура("Номенклатура,Склад");

Для Инд = 0 По ТЗ.Количество() - 1 Цикл

ТекущаяСтрока = ТЗ[Инд];

ЗаполнитьЗначенияСвойств(Отбор, ТекущаяСтрока);

Найденные = ТЗ.НайтиСтроки(Отбор);

Если Найденные.Количество() > 1 Тогда

// Удаляем все совпадения кроме первой

Для К = 1 По Найденные.Количество() - 1 Цикл

Если ТЗ.Индекс(Найденные[К]) > Инд Тогда

ТЗ.Удалить(Найденные[К]);

КонецЕсли;

КонецЦикла;

КонецЕсли;

КонецЦикла;

Такой подход сохраняет все колонки и порядок первых вхождений, но на больших таблицах работает медленно: НайтиСтроки() каждый раз просматривает таблицу целиком. Для десятков тысяч строк лучше использовать соответствие для учёта уже встреченных ключей.

bsl
УжеВстречались = Новый Соответствие;
Инд = ТЗ.Количество() - 1;
Пока Инд >= 0 Цикл
    Строка = ТЗ[Инд];
    Ключ = Строка(Строка.Номенклатура) + "|" + Строка(Строка.Склад);
    Если УжеВстречались[Ключ] = Неопределено Тогда
        УжеВстречались.Вставить(Ключ, Истина);
    Иначе
        ТЗ.Удалить(Инд);
    КонецЕсли;
    Инд = Инд - 1;
КонецЦикла;

Вариант с соответствием идёт с конца таблицы, поэтому при дублях остаётся последняя встречная строка. Если нужно оставить первую — идите с начала и удаляйте через ТЗ.Удалить(Инд) с корректировкой индекса (Инд = Инд - 1 после удаления).

Альтернатива: свёртка на уровне запроса

Если таблица значений получается из запроса, дубли правильнее устранять там же, а не постфактум. В языке запросов для этого есть два инструмента: РАЗЛИЧНЫЕ — убирает полные дубли строк результата, и группировка СГРУППИРОВАТЬ ПО с агрегатными функциями — для объединения по ключевым полям.

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

Товары.Номенклатура,

Товары.Склад

ИЗ

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

Преимущество запроса — скорость на больших объёмах и отсутствие промежуточного кода. Недостаток — меньше гибкости: сложную логику выбора «какую именно строку оставить» в запросе выразить труднее, чем в переборе.

Сравнение вариантов

Свернуть()

Подходит для полных дублей и свёртки с суммированием. Ограничение: удаляет неперечисленные колонки и меняет исходную таблицу.

Перебор с Соответствием

Подходит, когда нужно оставить строку целиком со всеми колонками. Ограничение: больше кода, нужно аккуратно работать с индексами.

РАЗЛИЧНЫЕ / группировка в запросе

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

Проверка результата и контрольные точки

После очистки полезно убедиться, что удалено именно то, что планировалось. Простейшая проверка — сравнить количество строк до и после и посчитать число удалённых. Если таблица не изменилась, либо дублей не было, либо критерий отбора задан неверно (например, колонка в структуре отбора названа с ошибкой — тогда НайтиСтроки() вернёт пустой массив или вызовет исключение).

Второй контроль — суммы по числовым колонкам. При свёртке с суммированием общий итог до и после должен совпасть. Если итог изменился, значит часть данных потерялась, и нужно пересмотреть список суммируемых колонок.

Контрольные точки

Как проверить результат

Количество строк после Свернуть()
Норма

Строк стало меньше или столько же, уникальные комбинации сохранены

Тревожный признак

Осталась одна строка или таблица пуста — вероятно, перепутаны параметры группировки

Итог по числовой колонке
Норма

Сумма до и после свёртки совпадает

Тревожный признак

Итог уменьшился — часть колонок не попала в суммируемые и была отброшена

Состав колонок
Норма

Все нужные колонки на месте

Тревожный признак

Колонки пропали — они не были перечислены в параметрах Свернуть()

Типичные ошибки и ограничения

Самая частая ошибка — удаление строк в прямом цикле Для Каждого. Изменение коллекции во время её перебора приводит к пропуску элементов или ошибке. Удалять нужно либо по индексу с конца, либо собирать строки в отдельный массив и удалять после цикла.

Вторая ошибка — сравнение ссылочных значений «на глаз». Две строки могут выглядеть одинаково в отладчике, но содержать разные ссылки (например, разные элементы справочника с одинаковым наименованием). Для Свернуть() и НайтиСтроки() они дублями не являются. Если нужно объединять по представлению, добавьте в таблицу отдельную колонку с наименованием и группируйте по ней.

Типичные ошибки
01
Удаление строк внутри цикла Для Каждого — часть дублей пропускается из-за сдвига коллекции
02
Вызов Свернуть() без копии таблицы — исходные данные теряются и нечем проверить результат
03
Сравнение дат с разным временем — строки не считаются дублями, хотя по смыслу совпадают; дату стоит привести через НачалоДня()
04
Соединение ключей через строку без разделителя — комбинации «АБ»+«В» и «А»+«БВ» дадут одинаковый ключ и ложно сольются

Ещё одно ограничение касается типов колонок. Свернуть() корректно работает с типами, допускающими сравнение на равенство; колонки с типом ХранилищеЗначения или нестандартными составами могут дать неожиданный результат. В сомнительных случаях проверяйте поведение на маленькой тестовой таблице.

Итог

Что важно запомнить

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

Частые вопросы

Удаляет ли Свернуть() исходную таблицу или создаёт новую?

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

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

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

Почему Свернуть() не убрал визуально одинаковые строки?

Чаще всего значения отличаются в неочевидном: лишние пробелы в строках, разное время в датах, разные ссылки с одинаковым представлением или различие регистра там, где сравнение его учитывает. Приведите значения к единому виду (СокрЛП, НачалоДня) перед свёрткой.

Что быстрее на большой таблице — Свернуть() или перебор с НайтиСтроки()?

Свернуть() существенно быстрее, так как реализован на уровне платформы. Перебор с НайтиСтроки() на каждой итерации сканирует таблицу целиком и на больших объёмах заметно проигрывает. Если перебор необходим, используйте соответствие для учёта ключей — это линейный по времени алгоритм.

Можно ли удалить дубли в табличной части документа теми же способами?

Да, табличная часть поддерживает те же методы Свернуть(), НайтиСтроки() и Удалить(). Учтите, что изменения вступят в силу только после записи документа, а свёртка табличной части с суммированием аналогично удалит неперечисленные колонки.

Как подсчитать количество дублей, не удаляя их?

Скопируйте таблицу, сверните копию по всем колонкам и сравните количество строк копии с исходным: разница и есть число удалённых повторов. Либо добавьте в копию числовую колонку со значением 1 и сверните с её суммированием — получите счётчик вхождений каждой комбинации.