Как обратиться к предыдущей строке при переборе таблицы в 1С

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

При переходе с платформы 1С 7.7 на 1С 8.x программисты часто сталкиваются с изменением парадигмы обхода коллекций. В старой версии обращение к строкам было процедурным (через метод ПолучитьСтроку()), а в актуальных версиях мы работаем с объектами и индексами. Одной из типовых задач является получение данных из предыдущей строки для расчета разницы дат, вычисления сальдо или накопленного итога. Рассмотрим подробно все способы решения этой задачи.

Метод 1. Использование индекса в цикле «Для»

Самый очевидный способ обратиться к предыдущему элементу — это использовать цикл со счетчиком. В 1С 8.x индексация коллекций (табличных частей, таблиц значений, массивов) начинается с 0. Это критически важно помнить, так как свойство НомерСтроки у табличных частей начинается с 1. Следовательно, индекс строки всегда на единицу меньше её номера: Индекс = НомерСтроки - 1.

Проанализируем пример кода, где нам нужно рассчитать количество дней между датами в текущей и предыдущей строках:


Для Инд = 1 По ТЧ.Количество() - 1 Цикл
    ТекущаяСтрока = ТЧ[Инд];
    ПредыдущаяСтрока = ТЧ[Инд - 1];
    
    // Вычисляем разницу в секундах и переводим в дни
    РазницаВДнях = (ТекущаяСтрока.ДатаДокумента - ПредыдущаяСтрока.ДатаДокумента) / (24 * 60 * 60);
    ТекущаяСтрока.Кол_во_Дней = РазницаВДнях;
КонецЦикла;

Разберем, почему мы начинаем цикл с 1, а не с 0. Если мы начнем с нуля, то при попытке обратиться к ТЧ[Инд - 1] система выдаст ошибку «Индекс находится за границами массива», так как индекса -1 не существует. Начиная со второй строки (индекс 1), мы гарантируем наличие предыдущего элемента.

Метод 2. Использование вспомогательной переменной (метод итератора)

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

Рассмотрим реализацию этого алгоритма:


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

Для каждого СтрокаТЧ Из ТЧ Цикл
    Если ПеременнаяПредСтрока = Неопределено Тогда
        // Это первая итерация, предыдущей строки еще нет
        СтрокаТЧ.ТекущееСальдо = СтрокаТЧ.СуммаРасход - СтрокаТЧ.СуммаПриход;
    Иначе
        // Работаем с данными из предыдущей итерации
        СтрокаТЧ.ТекущееСальдо = ПеременнаяПредСтрока.ТекущееСальдо + СтрокаТЧ.СуммаРасход - СтрокаТЧ.СуммаПриход;
        
        // Пример записи значения в ПРЕДЫДУЩУЮ строку
        ПеременнаяПредСтрока.Кол_во_Дней = (СтрокаТЧ.Дата - ПеременнаяПредСтрока.Дата) / 86400;
    КонецЕсли;
    
    // В конце итерации запоминаем текущую строку как «предыдущую» для следующего шага
    ПеременнаяПредСтрока = СтрокаТЧ;
КонецЦикла;

Этот метод хорош тем, что нам не нужно постоянно обращаться к коллекции по индексу, что экономит ресурсы системы при работе с очень большими таблицами.

Ловушка производительности: Метод Индекс()

Иногда разработчики пытаются получить индекс внутри цикла Для каждого, используя метод ТЧ.Индекс(СтрокаТЧ). Проанализируем, почему это плохая практика. Метод Индекс() каждый раз перебирает таблицу с самого начала, чтобы найти адрес конкретной строки. В результате сложность алгоритма возрастает в геометрической прогрессии. Для таблицы в 10 000 строк это приведет к ощутимому зависанию программы. Рекомендация: если вам нужен индекс, используйте цикл Для Счетчик = 0 По... или просто инкрементируйте переменную-счетчик внутри цикла Для каждого.

Особенности изменения значений и ошибка «Только для чтения»

Выясним причину часто возникающей ошибки «Индексированное значение доступно только для чтения». Она часто проявляется, когда программист пытается присвоить значение всей строке или неправильно обращается к коллекции. Например, попытка написать ТЧ[i] = НоваяСтрока вызовет ошибку. Нужно всегда указывать конкретное поле: ТЧ[i].ИмяПоля = Значение.

Также помните, что если ваша таблица является результатом запроса (РезультатЗапроса.Выбрать()), вы не сможете менять в ней данные. Для редактирования значений «на лету» результат запроса необходимо сначала выгрузить в таблицу значений методом Выгрузить().

Работа с предыдущими строками в запросах

Посмотрим на ситуацию шире: иногда задачу эффективнее решить на уровне базы данных. В стандартном SQL для этого существуют функции LAG и LEAD, но в языке запросов 1С их нет. Чтобы получить данные из соседней строки в запросе, мы применяем соединение таблицы самой с собой.

  1. Создаем временную таблицу с данными и добавляем в неё поле НомерСтроки (используя автоупорядочивание).
  2. Соединяем эту временную таблицу саму с собой по условию: Т1.НомерСтроки = Т2.НомерСтроки + 1.
  3. В результате в одной выборке у нас будут поля как текущей строки (Т1), так и предыдущей (Т2).

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

Использование Системы компоновки данных (СКД)

Если ваша цель — вывести расчет разницы между строками в отчете, воспользуйтесь встроенными функциями СКД. Функция ВычислитьВыражение() позволяет обращаться к любым строкам группировки.

Пример выражения для ресурса в СКД:

ВычислитьВыражение("Сумма(СуммаОборот)", "Период", , "Предыдущая", "Предыдущая")

Это выражение вернет сумму из предыдущей строки группировки по периоду без написания кода на языке 1С.

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

← На главную