Как перенести значение из одной строки Таблицы Значений в другую и удалить исходную

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

При разработке на платформе 1С:Предприятие часто возникает задача консолидации данных внутри ТаблицыЗначений. Например, когда необходимо объединить строки, показатели которых не достигают определенного порога, с последующими или предыдущими строками. В этой статье мы подробно разберем, как правильно реализовать этот механизм, избежав типичных ошибок вроде «Индекс находится за границами массива», и рассмотрим несколько подходов к решению.

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

Представим ситуацию: у нас есть таблица с колонкой Число. Нам нужно обойти таблицу и, если значение в текущей строке меньше 5, прибавить это значение к следующей строке, а текущую строку удалить. Основная сложность здесь заключается в том, что при удалении строки в процессе обхода индексы всех последующих строк смещаются, что приводит к некорректной работе цикла.

Проанализируем первый вариант решения, предложенный в обсуждении, который использует вспомогательный массив для удаления строк. Это позволяет избежать проблем со смещением индексов непосредственно в момент проверки условий.

Вариант 1: Использование вспомогательного массива и прямого цикла

Рассмотрим по шагам реализацию через создание массива ссылок на строки, которые подлежат удалению. Этот способ удобен тем, что мы сначала выполняем все математические операции, а затем очищаем таблицу.


// Создаем массив, куда будем складывать строки для удаления
СтрокиКУдалению = Новый Массив;

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

// Вторым проходом удаляем накопленные строки
Для каждого Стр Из СтрокиКУдалению Цикл
    ТЗ.Удалить(Стр);
КонецЦикла;

Важный нюанс: Обратите внимание на условие завершения цикла ТЗ.Количество() - 2. Если ваша таблица содержит 10 строк (индексы от 0 до 9), то на последней итерации Н будет равно 8. При этом выражение ТЗ[Н+1] обратится к 9-й строке (последней). Если бы мы использовали ТЗ.Количество() - 1, то при Н = 9 попытка обратиться к ТЗ[10] вызвала бы ошибку.

Вариант 2: Обратный обход таблицы (наиболее надежный метод)

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

Однако в нашей задаче есть особенность: значение нужно передавать следующей строке. Поэтому при обратном обходе мы будем передавать значение «вперед» по индексу, который уже был проверен. Разберем этот пример:


ВсегоСтрок = ТЗ.Количество();
Для Индекс = -(ВсегоСтрок - 1) По 0 Цикл
    Н = -Индекс; // Получаем индекс от последнего к первому
    
    // Проверяем, что это не последняя строка, так как ей некуда передавать значение
    Если Н < ВсегоСтрок - 1 Тогда
        СтрТекущая = ТЗ[Н];
        Если СтрТекущая.Число < 5 Тогда
            ТЗ[Н + 1].Число = ТЗ[Н + 1].Число + СтрТекущая.Число;
            ТЗ.Удалить(СтрТекущая);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Вариант 3: Решение проблемы последней строки

Разберем ситуацию, описанную в сообщении №3: что делать, если последняя строка таблицы оказалась меньше порогового значения? В стандартном алгоритме ей некуда передавать остаток. В таком случае логично передать значение предыдущей строке, чтобы общая сумма по колонке в рамках всей таблицы осталась неизменной.

Выясним причину, почему это важно: если мы просто удалим последнюю строку, данные будут потеряны. Посмотрим на пример кода с обработкой этого исключения:


Колво = ТЗ.Количество();
Если Колво > 1 Тогда
    СтрокиДляУдаления = Новый Массив;
    
    Для Н = 0 По Колво - 1 Цикл
        Стр = ТЗ[Н];
        
        Если Стр.Число < 5 Тогда
            Если Н < ТЗ.Количество() - 1 Тогда
                // Передаем вперед
                ТЗ[Н + 1].Число = ТЗ[Н + 1].Число + Стр.Число;
            Иначе
                // Это последняя строка, передаем назад
                // Находим последнюю оставшуюся строку, которая не помечена на удаление
                // Или просто прибавляем к предыдущей, если алгоритм простой
                ТЗ[Н - 1].Число = ТЗ[Н - 1].Число + Стр.Число;
            КонецЕсли;
            СтрокиДляУдаления.Добавить(Стр);
        КонецЕсли;
    КонецЦикла;
    
    Для каждого Стр Из СтрокиДляУдаления Цикл
        ТЗ.Удалить(Стр);
    КонецЦикла;
КонецЕсли;

Использование метода Свернуть() для группировки

Проанализируем альтернативный подход. Если ваша задача — не просто перекинуть остаток, а объединить данные по определенным ключам (например, по Сотруднику или Проекту), 1С предоставляет мощный встроенный метод Свернуть(). Он работает значительно быстрее ручного обхода.

Рассмотрим сценарий: нам нужно схлопнуть все записи, чтобы не было дублей, и при этом не важен порядок строк. Мы можем сначала программно изменить ключевые поля у «мелких» строк, а затем вызвать Свернуть. Но чаще всего этот метод используется просто для агрегации:

ТЗ.Свернуть("Сотрудник, Проект", "Число");

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

Оптимизация для больших таблиц

Если в вашей таблице десятки тысяч строк, метод ТЗ.Удалить() может вызвать замедление работы, так как при каждом удалении система пересчитывает индексы. В таком случае мы рекомендуем следующий алгоритм:

  1. Создаем копию структуры таблицы: НоваяТЗ = ТЗ.СкопироватьКолонки();
  2. Заводим переменную-аккумулятор НакопленныйОстаток = 0;
  3. Обходим исходную таблицу. Если Число + НакопленныйОстаток < 5, то просто прибавляем значение к НакопленныйОстаток.
  4. Если сумма >= 5, добавляем новую строку в НоваяТЗ, записываем туда сумму и обнуляем НакопленныйОстаток.
  5. В конце, если остаток не равен 0, прибавляем его к последней строке НоваяТЗ.

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

Заключение

Мы рассмотрели различные способы переноса значений между строками ТаблицыЗначений. Выбор конкретного метода зависит от объема ваших данных и требований к точности (нужно ли обрабатывать последнюю строку отдельно). Помните о следующих правилах:

← На главную