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