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