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

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

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

Способ 1. Использование метода НайтиСтроки() в цикле

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


// Предположим, у нас есть ИсходнаяТаблица с колонками: 
// Валюта, Контрагент, СуммаПлатежа, ЗаявкаНаОплату

// 1. Создаем копию таблицы для свертки
СвернутаяТаблица = ИсходнаяТаблица.Скопировать();

// 2. Сворачиваем таблицу по ключевым полям
// Валюта и Контрагент - измерения, СуммаПлатежа - ресурс
СвернутаяТаблица.Свернуть("Валюта, Контрагент", "СуммаПлатежа");

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

Важно помнить, что метод НайтиСтроки() работает достаточно быстро, если по колонкам поиска в таблице значений построен индекс. Перед началом цикла крайне рекомендуется использовать метод Индексы.Добавить("Валюта, Контрагент") для объекта ИсходнаяТаблица. Это значительно ускорит работу алгоритма на больших массивах данных.

Способ 2. Оптимизация через объект Соответствие (Map)

Если таблица содержит десятки тысяч строк, использование НайтиСтроки() в цикле может привести к заметным задержкам, так как сложность алгоритма приближается к квадратичной. Рассмотрим более изящный и быстрый способ с использованием объекта Соответствие. Этот метод позволяет пройтись по таблице всего один раз.

Выясним причину эффективности этого метода: поиск по ключу в объекте Соответствие (или Структура) происходит практически мгновенно. Мы можем использовать в качестве ключа уникальную комбинацию полей, а в качестве значения — строку или массив со списком заявок.


ХранилищеГруппировок = Новый Соответствие;

Для Каждого Стр Из ИсходнаяТаблица Цикл
    // Формируем уникальный ключ (можно через строку или структуру)
    Ключ = Стр.Валюта.УникальныйИдентификатор() + "_" + Стр.Контрагент.УникальныйИдентификатор();
    
    ТекДанные = ХранилищеГруппировок.Получить(Ключ);
    Если ТекДанные = Неопределено Тогда
        ТекДанные = Новый СписокЗначений;
        ХранилищеГруппировок.Вставить(Ключ, ТекДанные);
    КонецЕсли;
    
    // Добавляем заявку в список для этого ключа
    ТекДанные.Добавить(Стр.ЗаявкаНаОплату);
КонецЦикла;

// Теперь при выводе сообщения нам не нужно ничего искать, 
// данные уже распределены по «корзинам»

Способ 3. Группировка с помощью СКД и функции СоединитьСтроки

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

Разберем по шагам, как это работает в настройках ресурсов:

  1. Добавьте поле ЗаявкаНаОплату в ресурсы.
  2. В поле «Выражение» пропишите: СоединитьСтроки(Массив(ЗаявкаНаОплату), ", ").
  3. В настройках группировки укажите Валюта и Контрагент.

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

Способ 4. Метод сортировки и одного прохода

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

Алгоритм действий следующий:

  1. Отсортируйте исходную таблицу значений по ключевым колонкам (Валюта, Контрагент).
  2. Организуйте цикл Для по строкам таблицы.
  3. Сравнивайте значения ключевых колонок текущей строки с предыдущей.
  4. Пока значения совпадают — накапливайте список заявок в текстовую переменную.
  5. Как только значения изменились — выводите накопленное сообщение и сбрасывайте переменную.

ИсходнаяТаблица.Сортировать("Валюта, Контрагент");

ТекВалюта = Неопределено;
ТекКонтрагент = Неопределено;
СписокЗаявок = "";

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

// Не забываем вывести последнюю группу
Сообщить("Свернуты заявки: " + СписокЗаявок);

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

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

← На главную