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

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

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

Метод 1. Использование цикла по индексу

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

Проанализируем ситуацию: если в таблице N строк, то индексы идут от 0 до N-1. Если мы находимся на строке с индексом Индекс, то следующая строка будет иметь индекс Индекс + 1. Чтобы не выйти за границы массива при обращении к последнему элементу, цикл должен заканчиваться на предпоследнем элементе.

Разберем пример кода:


КоличествоСтрок = Объект.Товары.Количество();
// Перебираем строки до предпоследней (Количество - 2)
Для Индекс = 0 По КоличествоСтрок - 2 Цикл
    ТекущаяСтрока = Объект.Товары[Индекс];
    СледующаяСтрока = Объект.Товары[Индекс + 1];
    
    // Сравниваем нужное поле, например, НомерДокумента
    Если ТекущаяСтрока.НомерДокумента = СледующаяСтрока.НомерДокумента Тогда
        // Логика, если значения совпадают
    Иначе
        // Логика, если значения различаются
    КонецЕсли;
КонецЦикла;

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

Метод 2. Использование вспомогательной переменной в цикле "Для каждого"

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

Посмотрим на алгоритм реализации такого подхода:


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

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

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

Метод 3. Обработка данных при импорте из Excel (Кейс-стади)

Рассмотрим конкретную задачу: данные из Excel загружены в ТЗ, и нужно создать несколько документов "Реализация товаров и услуг". В таблице есть колонки с данными шапки и данными строк ТЧ. Строки сгруппированы по уникальному идентификатору документа из файла.

Проанализируем по шагам эффективный алгоритм решения:

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

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


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

// Не забываем записать самый последний документ после цикла
Если НовыйДокумент <> Неопределено Тогда
    НовыйДокумент.Записать();
КонецЕсли;

Метод 4. Обратный цикл при необходимости удаления

Выясним важную причину, по которой обычный цикл "вперед" (от 0 до N) может не подойти. Если в процессе сравнения вы решите, что текущая или следующая строка должна быть удалена, индексы всех последующих строк сместятся. Это приведет к пропуску следующей строки или ошибке выполнения.

Золотое правило: Если предполагается удаление строк, используйте обратный цикл.


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

// Или более привычный вариант через "Пока":
к = Объект.Товары.Количество() - 1;
Пока к > 0 Цикл
    Текущая = Объект.Товары[к];
    Предыдущая = Объект.Товары[к-1];
    
    Если Текущая.Дубль = Истина Тогда
        Объект.Товары.Удалить(к);
    КонецЕсли;
    
    к = к - 1;
КонецЦикла;

Производительность и типичные ошибки

Разберем несколько нюансов, которые помогут сделать ваш код быстрее и надежнее:

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

← На главную