Разработка и Конфигурация · 13 мин
Как свернуть массив структур в 1С: группировка и суммирование
Как свернуть массив структур в 1С: группировка по ключевым полям, суммирование показателей, примеры кода на встроенном языке и типичные ошибки разработчиков.
Массив структур — один из самых частых форматов промежуточных данных в 1С: так возвращают результаты обмены, разборы JSON, выборки из внешних источников и результаты собственных функций. Задача «свернуть» такой массив возникает, когда нужно сгруппировать строки по одному или нескольким полям и посчитать итоги по числовым показателям — аналогично тому, как метод Свернуть() работает у таблицы значений.
Проблема в том, что у объекта «Массив» во встроенном языке 1С нет метода свёртки. Поэтому разработчику приходится выбирать обходные пути: перегонять данные в таблицу значений, сворачивать вручную через соответствие или использовать запрос. Каждый вариант имеет свою цену по производительности и объёму кода.
Ниже разобраны рабочие подходы с готовым кодом, критерии выбора между ними, проверка результата и типичные ошибки, которые приводят к потере данных или неверным итогам.
Почему массив структур нельзя свернуть напрямую
Объект «Массив» во встроенном языке 1С — это просто упорядоченная коллекция значений. У него есть методы Добавить(), Вставить(), Удалить(), Найти(), но нет ни группировки, ни агрегации. Структуры внутри массива для платформы — независимые объекты, она не знает, какие поля считать ключевыми, а какие — суммируемыми.
Метод Свернуть() существует только у таблицы значений и у табличной части. Он группирует строки по указанным колонкам и суммирует значения в числовых колонках ресурсов. Поэтому большинство решений сводится к тому, чтобы привести данные к таблице значений, выполнить свёртку и, при необходимости, вернуть результат обратно в массив структур.
Способ 1. Через таблицу значений — универсальный и быстрый
Это основной рекомендуемый подход для массивов любого размера. Сначала создаётся таблица значений с нужными колонками, затем в неё переносятся все структуры, после чего вызывается Свернуть().
Функция СвернутьМассивСтруктур(МассивСтруктур)
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Контрагент", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Для Каждого Элемент Из МассивСтруктур Цикл
СтрокаТЗ = ТЗ.Добавить();
ЗаполнитьЗначенияСвойств(СтрокаТЗ, Элемент);
КонецЦикла;
ТЗ.Свернуть("Контрагент", "Сумма,Количество");
Возврат ТЗ;
КонецФункции
Обратите внимание на три момента. Во-первых, типы колонок задавайте явно: если колонка «Сумма» получит составной тип или тип «Строка», суммирование будет работать некорректно или не будет работать вовсе. Во-вторых, ЗаполнитьЗначенияСвойств переносит только те свойства структуры, имена которых совпадают с именами колонок, — лишние поля структуры просто игнорируются. В-третьих, после свёртки в таблице останутся только колонки группировки и суммируемые колонки — остальные будут удалены.
Возврат результата обратно в массив структур
Если дальше по коду нужен именно массив структур (например, для сериализации в JSON или возврата из функции), свёрнутую таблицу легко преобразовать обратно:
Функция ТаблицуВМассивСтруктур(ТЗ)
Результат = Новый Массив;
ИменаКолонок = Новый Массив;
Для Каждого Колонка Из ТЗ.Колонки Цикл
ИменаКолонок.Добавить(Колонка.Имя);
КонецЦикла;
Для Каждого СтрокаТЗ Из ТЗ Цикл
Элемент = Новый Структура;
Для Каждого ИмяКолонки Из ИменаКолонок Цикл
Элемент.Вставить(ИмяКолонки, СтрокаТЗ[ИмяКолонки]);
КонецЦикла;
Результат.Добавить(Элемент);
КонецЦикла;
Возврат Результат;
КонецФункции
- 1Создайте таблицу значений и явно опишите колонки с типами
- 2Перенесите элементы массива в таблицу через ЗаполнитьЗначенияСвойств
- 3Вызовите Свернуть() со списком полей группировки и суммируемых полей
- 4Проверьте итоги: сумма по свёрнутой таблице должна равняться сумме по исходному массиву
- 5При необходимости преобразуйте результат обратно в массив структур
Способ 2. Свёртка вручную через соответствие
Для небольших массивов (десятки и сотни элементов) или когда нельзя создавать таблицу значений, свёртку можно выполнить напрямую через объект «Соответствие». Ключом служит значение поля группировки (или комбинация полей), значением — структура-аккумулятор.
Функция СвернутьЧерезСоответствие(МассивСтруктур)
Группы = Новый Соответствие;
Для Каждого Элемент Из МассивСтруктур Цикл
Ключ = Элемент.Контрагент;
Накоплено = Группы.Получить(Ключ);
Если Накоплено = Неопределено Тогда
Накоплено = Новый Структура("Контрагент,Сумма,Количество", Ключ, 0, 0);
Группы.Вставить(Ключ, Накоплено);
КонецЕсли;
Накоплено.Сумма = Накоплено.Сумма + Элемент.Сумма;
Накоплено.Количество = Накоплено.Количество + Элемент.Количество;
КонецЦикла;
Результат = Новый Массив;
Для Каждого КлючЗначение Из Группы Цикл
Результат.Добавить(КлючЗначение.Значение);
КонецЦикла;
Возврат Результат;
КонецФункции
Если группировка идёт по нескольким полям, ключ соответствия формируют составным — например, через конкатенацию с разделителем, который гарантированно не встречается в данных: Ключ = Элемент.Склад + "|" + Элемент.Номенклатура. Более надёжный вариант — использовать в качестве ключа другую структуру нельзя (соответствие сравнивает структуры по ссылке, а не по содержимому), поэтому составной ключ собирают строкой или используют вложенные соответствия.
Способ 3. Через запрос, если данные уже можно получить запросом
Если массив структур строится из данных информационной базы, часто правильнее вообще отказаться от промежуточного массива и сразу выполнить запрос с группировкой СГРУППИРОВАТЬ ПО и агрегатными функциями СУММА(), КОЛИЧЕСТВО(), МАКСИМУМ(). Это самый быстрый вариант, потому что группировка выполняется на стороне СУБД.
Если массив уже сформирован, его можно передать в запрос через таблицу значений как параметр (временная таблица) и свернуть там. Это оправдано, когда дальше результат нужно соединить с данными базы — например, подтянуть остатки или реквизиты справочников.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ.Контрагент КАК Контрагент,
| ВТ.Сумма КАК Сумма
|ПОМЕСТИТЬ ВТДанные
|ИЗ &ТЗ КАК ВТ
|;
|ВЫБРАТЬ
| ВТДанные.Контрагент КАК Контрагент,
| СУММА(ВТДанные.Сумма) КАК Сумма
|ИЗ ВТДанные КАК ВТДанные
|СГРУППИРОВАТЬ ПО ВТДанные.Контрагент";
Запрос.УстановитьПараметр("ТЗ", ТаблицаИзМассива);
Результат = Запрос.Выполнить().Выгрузить();
Сравнение способов свёртки
Универсальный вариант для любых объёмов. Требует описания колонок и типов, но код короткий и надёжный.
Подходит для небольших массивов и нестандартной агрегации (не только сумма). Больше кода, легко ошибиться в составном ключе.
Лучший выбор, если данные из базы или нужно соединение с другими таблицами. Избыточен для простой свёртки готового массива.
Нестандартная агрегация: не только сумма
Метод Свернуть() умеет только суммировать. Если требуется посчитать количество уникальных значений, максимум, минимум, среднее или собрать строковые значения через запятую, придётся использовать ручную свёртку через соответствие или запрос.
Для максимума и минимума в ручной свёртке аккумулятор сравнивается с текущим значением. Для подсчёта количества строк группы заводится отдельное числовое поле-счётчик. Для среднего хранят сумму и количество, а деление выполняют после прохода по всему массиву — так избегают накопления ошибок округления.
Если нужно собрать в группу список значений (например, все номера документов по контрагенту), аккумулятором делают массив или строку с разделителем. Учтите, что конкатенация длинных строк в цикле — дорогая операция; для больших объёмов лучше собирать значения в массив, а в конце соединить через СтрСоединить().
Проверка результата свёртки
После любой свёртки полезно выполнить контрольные проверки, особенно если данные дальше уходят в документы или отчёты. Главный контроль — сходимость итогов: сумма по суммируемому полю всех элементов исходного массива должна равняться сумме по свёрнутому результату. Расхождение означает, что часть строк потеряна (например, из-за ошибки в ключе группировки) или задвоена.
Вторая проверка — количество групп. Если вы ожидаете N уникальных значений поля группировки, а получили больше, вероятно, значения отличаются незаметно: лишние пробелы, разный регистр, неразрывные пробелы из выгрузок. Перед группировкой строковые ключи стоит нормализовать через СокрЛП(), а при необходимости — привести к одному регистру.
Как проверить результат свёртки
Итоги совпадают до копейки
Есть расхождение — часть строк потеряна или продублирована
Равно числу уникальных комбинаций ключевых полей
Больше ожидаемого — ключи различаются пробелами или регистром
Число в колонке таблицы значений или в аккумуляторе
Строка или Неопределено — итоги неверны или код падает
Типичные ошибки и ограничения
Самая частая ошибка — ожидание, что Свернуть() сохранит несуммируемые колонки. Он их удаляет. Если нужно сохранить, например, наименование контрагента вместе с его кодом, включите наименование в список полей группировки — тогда группировка пойдёт по паре «код + наименование», что обычно безопасно при однозначном соответствии.
Вторая ошибка — суммирование по колонке без явного типа. Если колонка таблицы значений создана без описания типов, а первое добавленное значение было строкой, итоги будут неверными. Третья — изменение исходного массива во время обхода: удаление элементов из массива внутри цикла Для Каждого приводит к пропуску элементов.
Ограничение по производительности: перегонка очень больших массивов (сотни тысяч элементов) в таблицу значений через построчное заполнение занимает заметное время. В таких случаях стоит пересмотреть архитектуру — возможно, данные правильнее сразу собирать в таблицу значений или обрабатывать запросом, минуя массив структур.
FAQ
Можно ли свернуть массив структур без таблицы значений?
Да, через соответствие: ключ — значение поля группировки (или составной ключ с разделителем), значение — структура-аккумулятор, в которой накапливаются суммы. Этот способ подходит для небольших массивов и для нестандартной агрегации, где Свернуть() недостаточно.
Как сгруппировать массив структур по двум и более полям?
В таблице значений перечислите поля через запятую: ТЗ.Свернуть("Склад,Номенклатура", "Количество"). При ручной свёртке соберите составной строковый ключ с разделителем, которого нет в данных, либо используйте вложенные соответствия — по одному уровню на каждое поле.
Почему после Свернуть() пропали нужные колонки?
Так устроен метод: в результате остаются только колонки группировки и суммируемые колонки. Чтобы сохранить дополнительное поле, включите его в список группировки — если оно однозначно зависит от ключа (например, наименование от кода), число групп не изменится.
Как посчитать не сумму, а количество строк в группе?
При свёртке через таблицу значений добавьте колонку-счётчик типа Число, заполните её единицей для каждой строки и укажите её как суммируемую. После Свернуть() в ней будет количество строк группы. В ручной свёртке просто увеличивайте поле-счётчик аккумулятора на единицу.
Что делать с полями, содержащими Неопределено?
Перед суммированием заменяйте Неопределено на 0, иначе сложение вызовет ошибку. При переносе в таблицу значений Неопределено в числовой колонке допустимо, но безопаснее нормализовать данные заранее — например, конструкцией: Сумма = ?(Элемент.Сумма = Неопределено, 0, Элемент.Сумма).
Что быстрее: таблица значений или соответствие?
На небольших объёмах разница незаметна. На больших массивах таблица значений со Свернуть() обычно работает быстрее, так как свёртка выполняется оптимизированным кодом платформы. Если данные изначально из базы, быстрее всего сгруппировать их запросом, вообще не создавая массив структур.
Что важно запомнить
Метода свёртки у массива нет: используйте таблицу значений со Свернуть() для сумм, соответствие — для ручной и нестандартной агрегации, запрос — если данные из базы. Задавайте типы колонок явно, нормализуйте строковые ключи и всегда сверяйте итоги до и после группировки.