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

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

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

Анализ ошибок в коде с индексным обходом

Рассмотрим исходный фрагмент кода, в котором выполняется попытка сравнения строк в цикле:


КоличествоСтрок = ТаблицаДанных.Количество();
Для а = 0 по КоличествоСтрок - 1 Цикл
    ТекСтрока = ТаблицаДанных.Получить(а);
    б = а + 1;
    СледСтрока = ТаблицаДанных.Получить(б);
    // Проверка условий
КонецЦикла;

В этой конструкции присутствуют критические ошибки, приводящие к аварийному завершению программы:

  1. Выход за границы массива: цикл выполняется до КоличествоСтрок - 1. На последней итерации переменная а равна индексу последней строки, а переменная б = а + 1 принимает значение, равное общему количеству строк. Попытка выполнить ТаблицаДанных.Получить(б) вызывает исключение «Индекс находится за границами массива».
  2. Необходимость сортировки: последовательное сравнение элементов а и а + 1 обнаружит одинаковые значения только в том случае, если таблица предварительно упорядочена по проверяемому полю. Без вызова метода ТаблицаДанных.Сортировать("razdelucheta Возр") дубликаты, расположенные в разных частях таблицы, найдены не будут.
  3. Избыточный поиск индекса колонки: вызов конструкции ТаблицаДанных.Колонки.Индекс(ТаблицаДанных.Колонки.Найти("razdelucheta")) перегружает код. Если колонка не найдена, метод Найти() вернет Неопределено, что приведет к ошибке вызова метода контекста Индекс.

Прямое обращение к значению колонки строки

В платформе 1С строка таблицы значений представляет собой объект типа СтрокаТаблицыЗначений. Обращаться к колонкам можно напрямую через имя свойства или через строковый индекс без предварительного вычисления числового номера колонки.

Посмотрим на корректные варианты синтаксиса сравнения:


// Вариант 1: прямое обращение через точку (рекомендуемый)
Если ТекСтрока.razdelucheta = СледСтрока.razdelucheta Тогда
    // Значения совпадают
КонецЕсли;

// Вариант 2: обращение по строковому имени колонки
Если ТекСтрока["razdelucheta"] = СледСтрока["razdelucheta"] Тогда
    // Значения совпадают
КонецЕсли;

Способ 1: Безопасный цикл с запоминанием предыдущей строки

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

Разберем по шагам реализацию данного алгоритма:


// Обязательно сортируем таблицу по проверяемой колонке
ТаблицаДанных.Сортировать("razdelucheta Возр");

ПредыдущаяСтрока = Неопределено;

Для Каждого ТекСтрока Из ТаблицаДанных Цикл
    Если ПредыдущаяСтрока <> Неопределено Тогда
        Если ТекСтрока.razdelucheta = ПредыдущаяСтрока.razdelucheta Тогда
            // Обнаружено совпадение с предыдущей строкой
        Иначе
            // Значение изменилось
        КонецЕсли;
    КонецЕсли;
    
    ПредыдущаяСтрока = ТекСтрока;
КонецЦикла;

Способ 2: Поиск повторяющихся значений через объект «Соответствие»

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

Проанализируем пример реализации:


УникальныеЗначения = Новый Соответствие;

Для Каждого Стр Из ТаблицаДанных Цикл
    ЗначениеКолонки = Стр.razdelucheta;
    
    Если УникальныеЗначения[ЗначениеКолонки] <> Неопределено Тогда
        // Значение уже встречалось ранее в таблице (дубликат)
    Иначе
        УникальныеЗначения.Вставить(ЗначениеКолонки, Истина);
    КонецЕсли;
КонецЦикла;

Способ 3: Использование методов «Скопировать» и «Свернуть»

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

Разберем данный подход:


// Создаем копию исходной таблицы, чтобы не повредить основные данные
ТЗ_Анализ = ТаблицаДанных.Скопировать();
ТЗ_Анализ.Колонки.Добавить("КоличествоДублей", Новый ОписаниеТипов("Число"));
ТЗ_Анализ.ЗаполнитьЗначения(1, "КоличествоДублей");

// Сворачиваем по проверяемой колонке и суммируем счетчик
ТЗ_Анализ.Свернуть("razdelucheta", "КоличествоДублей");

// Отбираем только те значения, которые встретились более одного раза
ОтборДублей = Новый Структура("razdelucheta");
Для Каждого Стр Из ТЗ_Анализ Цикл
    Если Стр.КоличествоДублей > 1 Тогда
        ОтборДублей.razdelucheta = Стр.razdelucheta;
        СтрокиВИсходнойТЗ = ТаблицаДанных.НайтиСтроки(ОтборДублей);
        // Обработка найденных строк в исходной таблице
    КонецЕсли;
КонецЦикла;

Способ 4: Запрос к таблице значений

Для таблиц с типизированными колонками наиболее наглядным и гибким решением выступает передача таблицы значений в качестве параметра во временную таблицу запроса с использованием секции ИМЕЮЩИЕ.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЗ.razdelucheta КАК razdelucheta,
    |    КОЛИЧЕСТВО(*) КАК КоличествоПовторов
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |    &ТаблицаДанных КАК ТЗ
    |СГРУППИРОВАТЬ ПО
    |    ТЗ.razdelucheta
    |ИМЕЮЩИЕ
    |    КОЛИЧЕСТВО(*) > 1";

Запрос.УстановитьПараметр("ТаблицаДанных", ТаблицаДанных);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();

Особенности сравнения данных: при сравнении строковых значений оператор = во встроенном языке 1С не учитывает регистр символов и игнорирует хвостовые пробелы. Если в колонке могут находиться ссылки или объекты разных типов, для сопоставления строковых представлений используйте явное приведение: Строка(ТекСтрока.razdelucheta).

← На главную