Массив структур 1С: создание, поиск, сортировка и JSON

Дата: 10.10.2026 08:27:51
Массив
Структура
Коллекции значений
JSON
Клиент-сервер
Встроенный язык
Комментарий:
0
Платформа:
8.3
Конфигурация:
Любая
Формы:
Любые

Массив структур 1С — массив, каждый элемент которого — структура с одинаковыми полями: Артикул, Количество, Цена. Это таблица без объекта «таблица»: элементы — строки, ключи — колонки. Его выбирают там, где таблица значений недоступна или мешает: в клиентском коде, при обмене между клиентом и сервером, в JSON. Ниже — создание, поиск, уникальность, группировка, сортировка через временную таблицу значений, передача на сервер и JSON. Основной код собран в два общих модуля.

Платформа 8.3; СтрРазделить и СтрСоединить требуют режима совместимости не ниже 8.3.6, объекты JSON — платформы 8.3.6 и новее.

Где встречается и чем удобнее таблицы значений

  • Клиентский код управляемой формы: на тонком и веб-клиенте таблицы значений нет, а массив и структура есть.
  • Обмен с сервером: серверная функция возвращает выборку на клиент массивом структур. В БСП для этого есть ОбщегоНазначения.ТаблицаЗначенийВМассив, но её описание предупреждает: для таблиц с большим количеством строк она не рекомендуется.
  • JSON: ПрочитатьJSON превращает массив объектов в массив структур, ЗаписатьJSON пишет его обратно.
Массив структурТаблица значений
Тонкий и веб-клиентдоступеннедоступна
Возврат с сервера на тонкий клиентда, если значения полей передаютсянет, только через реквизит формы
ПрочитатьJSON и ЗаписатьJSONнапрямуючерез массив структур
Разный набор полей у элементовдопустимколонки общие
Поиск, сортировка, свёрткасвоим кодом, он нижеНайтиСтроки, Сортировать, Свернуть
Типы колонок, параметр запросанетесть

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

Как создать массив структур в 1С

Элементы добавляют методом Добавить(), структуру создают конструктором: ключи через запятую, затем значения. Правило одно — новая структура на каждый элемент. Структура, созданная до цикла, попадёт в массив несколько раз одним и тем же объектом, и все элементы покажут данные последней строки.

// Общий модуль или модуль обработки. Только Массив и Структура,
// поэтому код работает и на клиенте, и на сервере.

// Строки - коллекция с полями Артикул и Количество:
// табличная часть, таблица значений, массив структур.
Функция ПозицииИзСтрок(Строки) Экспорт

	Позиции = Новый Массив;
	Для Каждого СтрокаКоллекции Из Строки Цикл
		// Новая структура на каждой итерации. Созданная один раз до цикла,
		// она попала бы в массив много раз - одним и тем же объектом
		Позиция = Новый Структура("Артикул, Количество");
		ЗаполнитьЗначенияСвойств(Позиция, СтрокаКоллекции);
		Позиции.Добавить(Позиция);
	КонецЦикла;

	Возврат Позиции;

КонецФункции

Функция ЗаказСПозициями() Экспорт

	Позиции = Новый Массив;
	Позиции.Добавить(Новый Структура("Артикул, Количество, Цена", "A-100", 2, 150));
	Позиции.Добавить(Новый Структура("Артикул, Количество, Цена", "B-205", 1, 990));
	Позиции.Добавить(Новый Структура("Артикул, Количество, Цена", "A-100", 3, 150));

	// Значение структуры - массив: заказ хранит список позиций
	Заказ = Новый Структура("Номер, Позиции, Сумма", "ЗК-0125", Позиции, 0);

	// Переменная цикла указывает на ту же структуру, что лежит в массиве:
	// новое поле появится у самого элемента
	Для Каждого Позиция Из Заказ.Позиции Цикл
		Позиция.Вставить("Сумма", Позиция.Количество * Позиция.Цена);
		Заказ.Сумма = Заказ.Сумма + Позиция.Сумма;
	КонецЦикла;

	// Поле элемента: индекс массива, затем ключ структуры
	Если Заказ.Позиции.Количество() > 0 Тогда
		Заказ.Вставить("ПервыйАртикул", Заказ.Позиции[0].Артикул);
	КонецЕсли;

	Возврат Заказ;

КонецФункции

Значение структуры — массив

Поле структуры может хранить массив, а его элементы — снова структуры: так выглядят заказ с позициями и разобранный JSON. Обращение идёт по цепочке: Заказ.Позиции[0].Артикул. Перед обращением по индексу проверяйте Количество(), иначе пустой массив даст ошибку «Индекс находится за границами массива».

Деталь цикла в ЗаказСПозициями: в массиве простых значений присваивание переменной цикла элемент не меняет (см. статью про массив в 1С), а здесь переменная указывает на ту же структуру, что лежит в массиве, и Позиция.Вставить("Сумма", …) меняет сам элемент.

Поиск в массиве структур, уникальность и группировка

Массив.Найти() здесь бесполезен: структуры он сравнивает как объекты, а не по содержимому. Поиск, отбор, выгрузку поля, удаление повторов, группировку, свёртку и сортировку без сервера собирает общий модуль МассивСтруктурКлиентСервер с флажками «Клиент (управляемое приложение)», «Сервер» и «Внешнее соединение». Элементом массива может быть структура или соответствие.

// Общий модуль МассивСтруктурКлиентСервер
// Флажки: Клиент (управляемое приложение), Сервер, Внешнее соединение;
// для обычных форм - ещё Клиент (обычное приложение). Вызов сервера снят.
// Элемент массива - Структура или Соответствие.
// СтрРазделить: режим совместимости конфигурации не ниже 8.3.6.

#Область ПрограммныйИнтерфейс

// Значение поля элемента или Неопределено, если такого поля нет.
Функция ЗначениеПоля(Элемент, ИмяПоля) Экспорт

	ТипЭлемента = ТипЗнч(Элемент);
	Если ТипЭлемента = Тип("Соответствие") Или ТипЭлемента = Тип("ФиксированноеСоответствие") Тогда
		Возврат Элемент.Получить(ИмяПоля);
	КонецЕсли;

	Значение = Неопределено;
	Элемент.Свойство(ИмяПоля, Значение);
	Возврат Значение;

КонецФункции

// Первый элемент, у которого поле равно значению, или Неопределено.
Функция НайтиПоПолю(Массив, ИмяПоля, Значение) Экспорт

	Для Каждого Элемент Из Массив Цикл
		Если ЗначениеПоля(Элемент, ИмяПоля) = Значение Тогда
			Возврат Элемент;
		КонецЕсли;
	КонецЦикла;

	Возврат Неопределено;

КонецФункции

// Все элементы, совпавшие со всеми полями отбора, - как НайтиСтроки у таблицы.
// Отбор - Структура, например Новый Структура("Склад, Оплачен", Склад, Истина).
Функция НайтиПоОтбору(Массив, Отбор) Экспорт

	Результат = Новый Массив;
	Для Каждого Элемент Из Массив Цикл
		Подходит = Истина;
		Для Каждого Условие Из Отбор Цикл
			Если ЗначениеПоля(Элемент, Условие.Ключ) <> Условие.Значение Тогда
				Подходит = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Подходит Тогда
			Результат.Добавить(Элемент);
		КонецЕсли;
	КонецЦикла;

	Возврат Результат;

КонецФункции

// Соответствие "значение поля -> первый элемент" для многократного поиска.
Функция ИндексПоПолю(Массив, ИмяПоля) Экспорт

	Индекс = Новый Соответствие;
	Для Каждого Элемент Из Массив Цикл
		Ключ = ЗначениеПоля(Элемент, ИмяПоля);
		Если Индекс.Получить(Ключ) = Неопределено Тогда
			Индекс.Вставить(Ключ, Элемент);
		КонецЕсли;
	КонецЦикла;

	Возврат Индекс;

КонецФункции

// Значения поля массивом, как ВыгрузитьКолонку у таблицы значений.
Функция ВыгрузитьПоле(Массив, ИмяПоля, БезПовторов = Ложь) Экспорт

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

	Возврат Результат;

КонецФункции

// Новый массив без повторов по полю: остаётся первый элемент с каждым значением.
Функция УникальныеПоПолю(Массив, ИмяПоля) Экспорт

	Результат = Новый Массив;
	Встречались = Новый Соответствие;
	Для Каждого Элемент Из Массив Цикл
		Ключ = ЗначениеПоля(Элемент, ИмяПоля);
		Если Встречались.Получить(Ключ) = Неопределено Тогда
			Встречались.Вставить(Ключ, Истина);
			Результат.Добавить(Элемент);
		КонецЕсли;
	КонецЦикла;

	Возврат Результат;

КонецФункции

// Соответствие "значение поля -> массив элементов с этим значением".
Функция Сгруппировать(Массив, ИмяПоля) Экспорт

	Группы = Новый Соответствие;
	Для Каждого Элемент Из Массив Цикл
		Ключ = ЗначениеПоля(Элемент, ИмяПоля);
		Группа = Группы.Получить(Ключ);
		Если Группа = Неопределено Тогда
			Группа = Новый Массив;
			Группы.Вставить(Ключ, Группа);
		КонецЕсли;
		Группа.Добавить(Элемент);
	КонецЦикла;

	Возврат Группы;

КонецФункции

// Свёртка, как Свернуть у таблицы значений: новый массив структур с полями
// группировки и суммами. Группы - в порядке первого появления.
// Пример: Свернуть(Позиции, "Склад, Номенклатура", "Количество, Сумма").
Функция Свернуть(Массив, ПоляГруппировки, ПоляСуммирования = "") Экспорт

	Группировки = ИменаПолей(ПоляГруппировки);
	Суммируемые = ИменаПолей(ПоляСуммирования);
	Если Группировки.Количество() = 0 Тогда
		ВызватьИсключение "Свернуть: не заданы поля группировки";
	КонецЕсли;
	Последнее = Группировки.ВГраница();

	Результат = Новый Массив;
	Корень = Новый Соответствие;

	Для Каждого Элемент Из Массив Цикл

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

		// На последнем уровне лежит сама итоговая структура
		Ключ = ЗначениеПоля(Элемент, Группировки[Последнее]);
		Итог = Узел.Получить(Ключ);
		Если Итог = Неопределено Тогда
			Итог = Новый Структура;
			Для Каждого ИмяПоля Из Группировки Цикл
				Итог.Вставить(ИмяПоля, ЗначениеПоля(Элемент, ИмяПоля));
			КонецЦикла;
			Для Каждого ИмяПоля Из Суммируемые Цикл
				Итог.Вставить(ИмяПоля, 0);
			КонецЦикла;
			Узел.Вставить(Ключ, Итог);
			Результат.Добавить(Итог);
		КонецЕсли;

		Для Каждого ИмяПоля Из Суммируемые Цикл
			Слагаемое = ЗначениеПоля(Элемент, ИмяПоля);
			Если ТипЗнч(Слагаемое) = Тип("Число") Тогда
				Итог[ИмяПоля] = Итог[ИмяПоля] + Слагаемое;
			КонецЕсли;
		КонецЦикла;

	КонецЦикла;

	Возврат Результат;

КонецФункции

// Сортировка вставками на месте, без вызова сервера. Устойчивая:
// элементы с равным значением поля сохраняют взаимный порядок.
// Значения поля - одного типа: Число, Строка или Дата. Для сотен элементов.
Процедура СортироватьПоПолю(Массив, ИмяПоля, ПоУбыванию = Ложь) Экспорт

	Для Индекс = 1 По Массив.ВГраница() Цикл
		Текущий = Массив[Индекс];
		Значение = ЗначениеПоля(Текущий, ИмяПоля);
		Позиция = Индекс - 1;
		Пока Позиция >= 0 И СтоитПосле(ЗначениеПоля(Массив[Позиция], ИмяПоля), Значение, ПоУбыванию) Цикл
			Массив[Позиция + 1] = Массив[Позиция];
			Позиция = Позиция - 1;
		КонецЦикла;
		Массив[Позиция + 1] = Текущий;
	КонецЦикла;

КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// "Склад, Номенклатура" -> массив имён полей без пробелов.
Функция ИменаПолей(СписокПолей)

	Имена = Новый Массив;
	Для Каждого Имя Из СтрРазделить(СписокПолей, ",", Ложь) Цикл
		Если Не ПустаяСтрока(Имя) Тогда
			Имена.Добавить(СокрЛП(Имя));
		КонецЕсли;
	КонецЦикла;

	Возврат Имена;

КонецФункции

Функция СтоитПосле(Левое, Правое, ПоУбыванию)

	Если ПоУбыванию Тогда
		Возврат Левое < Правое;
	КонецЕсли;

	Возврат Левое > Правое;

КонецФункции

#КонецОбласти

Разбор ключевых строк

  • ЗначениеПоля читает поле через Свойство() или Получить(). Нет поля — вернётся Неопределено, а не ошибка «Поле объекта не обнаружено»: массивы из внешних систем часто «дырявые».
  • НайтиПоПолю перебирает элементы. Для многократного поиска один раз постройте ИндексПоПолю: поиск по ключу соответствия идёт без перебора.
  • НайтиПоОтбору повторяет НайтиСтроки таблицы значений: совпасть должны все поля отбора.
  • УникальныеПоПолю оставляет первый элемент с каждым значением и сохраняет порядок. ВыгрузитьПоле с параметром БезПовторов даёт готовый массив для условия В (&Список).
  • Сгруппировать возвращает соответствие «значение → массив элементов». Порядок групп не гарантирован; упорядоченный результат даёт Свернуть.
  • Свернуть строит вложенные соответствия, по уровню на поле, поэтому ссылки и даты сравниваются как есть. При склейке ключа в строку две ссылки с одинаковым наименованием слились бы в одну. Нечисловые слагаемые, например null из JSON, пропускаются.
  • СортироватьПоПолю — устойчивая сортировка вставками на месте. Операторы > и < требуют значений одного типа: Число, Строка или Дата. Время растёт квадратично, так что это вариант для сотен элементов на клиенте.

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

Сортировка массива структур через временную таблицу

Сортировки по полю элемента у массива нет. На сервере надёжнее всего временная таблица значений: в неё кладут только поля сортировки и номер элемента, сортируют методом Сортировать и собирают новый массив из исходных структур. Порядок задаётся строкой, как у таблицы: "Дата Убыв, Сумма". Модуль серверный — МассивСтруктурСервер с флажками «Сервер» и «Внешнее соединение»; в него же положим функцию для результата запроса.

// Общий модуль МассивСтруктурСервер
// Флажки: Сервер, Внешнее соединение; Вызов сервера снят.
// Запроса и таблицы значений на тонком клиенте нет,
// поэтому эти функции - в серверном модуле.

// Результат запроса -> массив структур с ключами по колонкам запроса.
// В отличие от таблицы значений такой массив можно вернуть на клиент.
Функция ИзРезультатаЗапроса(РезультатЗапроса) Экспорт

	Ключи = "";
	Для Каждого Колонка Из РезультатЗапроса.Колонки Цикл
		Ключи = Ключи + ?(Ключи = "", "", ",") + Колонка.Имя;
	КонецЦикла;

	Результат = Новый Массив;
	Выборка = РезультатЗапроса.Выбрать();
	Пока Выборка.Следующий() Цикл
		Элемент = Новый Структура(Ключи);
		ЗаполнитьЗначенияСвойств(Элемент, Выборка);
		Результат.Добавить(Элемент);
	КонецЦикла;

	Возврат Результат;

КонецФункции

// Новый массив из тех же структур в порядке сортировки.
// Порядок - строка, как у ТаблицаЗначений.Сортировать: "Дата Убыв, Сумма".
// ПоСсылке - Истина: ссылки сравниваются по значению ссылки, быстро;
//            Ложь: по представлению, за которым платформа обращается к базе.
Функция Отсортировать(Массив, Порядок, ПоСсылке = Ложь) Экспорт

	// Колонки - только поля сортировки и номер элемента в исходном массиве
	Таблица = Новый ТаблицаЗначений;
	ПоляСортировки = Новый Массив;
	Для Каждого Часть Из СтрРазделить(Порядок, ",", Ложь) Цикл
		Слова = СтрРазделить(СокрЛП(Часть), " ", Ложь);
		Если Слова.Количество() > 0 Тогда
			ПоляСортировки.Добавить(Слова[0]);
			Таблица.Колонки.Добавить(Слова[0]);
		КонецЕсли;
	КонецЦикла;
	Если ПоляСортировки.Количество() = 0 Тогда
		ВызватьИсключение "Отсортировать: не задан порядок";
	КонецЕсли;
	Таблица.Колонки.Добавить("НомерЭлемента");

	Для НомерЭлемента = 0 По Массив.ВГраница() Цикл
		СтрокаТаблицы = Таблица.Добавить();
		СтрокаТаблицы.НомерЭлемента = НомерЭлемента;
		Для Каждого ИмяПоля Из ПоляСортировки Цикл
			СтрокаТаблицы[ИмяПоля] = МассивСтруктурКлиентСервер.ЗначениеПоля(Массив[НомерЭлемента], ИмяПоля);
		КонецЦикла;
	КонецЦикла;

	// Номер элемента - последний ключ: равные элементы сохранят исходный порядок
	Если ПоСсылке Тогда
		Таблица.Сортировать(Порядок + ", НомерЭлемента", Новый СравнениеЗначений);
	Иначе
		Таблица.Сортировать(Порядок + ", НомерЭлемента");
	КонецЕсли;

	Результат = Новый Массив;
	Для Каждого СтрокаТаблицы Из Таблица Цикл
		Результат.Добавить(Массив[СтрокаТаблицы.НомерЭлемента]);
	КонецЦикла;

	Возврат Результат;

КонецФункции
  • Структуры не копируются: в результат попадают те же объекты в новом порядке.
  • Номер элемента — последний ключ сортировки, поэтому равные элементы сохраняют исходный порядок.
  • Колонку со ссылками таблица по умолчанию сортирует по представлению и обращается за ним к базе — на тысячах элементов это заметно. ПоСсылке = Истина передаёт объект СравнениеЗначений: ссылки сравниваются по значению, быстро, но не по алфавиту. Для алфавита на большом массиве добавьте строковое поле с наименованием и сортируйте по нему.
  • Если массив собирается запросом, сортируйте в запросе: ИзРезультатаЗапроса сохранит порядок УПОРЯДОЧИТЬ ПО.

Передача между клиентом и сервером

Массив структур передаётся параметром серверной процедуры и возвращается на клиент, если передаются все значения внутри:

  • можно: Строка, Число, Дата, Булево, Неопределено, Null, ссылки, УникальныйИдентификатор, вложенные массивы, структуры, соответствия и списки значений;
  • нельзя: таблицы и деревья значений, СправочникОбъект, ДокументОбъект, запрос и его результат, данные формы — например, строку табличной части.

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

// Модуль формы документа с табличной частью Товары
// (Номенклатура, Количество, Сумма) и командой СводкаПоВыделенным.

&НаКлиенте
Процедура СводкаПоВыделенным(Команда)

	// Только нужные поля выделенных строк - в массив структур
	Позиции = Новый Массив;
	Для Каждого Идентификатор Из Элементы.Товары.ВыделенныеСтроки Цикл
		СтрокаТоваров = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
		Позиции.Добавить(Новый Структура("НомерСтроки, Номенклатура, Количество, Сумма",
			СтрокаТоваров.НомерСтроки, СтрокаТоваров.Номенклатура,
			СтрокаТоваров.Количество, СтрокаТоваров.Сумма));
	КонецЦикла;
	Если Позиции.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;

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

	// Итоги с сортировкой по наименованию - один внеконтекстный вызов
	Итоги = ИтогиПоНоменклатуре(Позиции);
	Для Каждого Итог Из Итоги Цикл
		Сообщить(Строка(Итог.Номенклатура) + ": " + Итог.Количество + " на сумму " + Итог.Сумма);
	КонецЦикла;

КонецПроцедуры

&НаСервереБезКонтекста
Функция ИтогиПоНоменклатуре(Знач Позиции)

	Итоги = МассивСтруктурКлиентСервер.Свернуть(Позиции, "Номенклатура", "Количество, Сумма");

	// Без СравнениеЗначений ссылки сортируются по представлению (обычно это наименование)
	Возврат МассивСтруктурСервер.Отсортировать(Итоги, "Номенклатура");

КонецФункции
  • Повторы ищутся на клиенте, без серверного вызова.
  • &НаСервереБезКонтекста передаёт на сервер только массив; контекстный &НаСервере отправил бы ещё и данные формы.
  • Без Знач массив после вызова вернулся бы с сервера на клиент — лишний трафик.
  • На клиент приходят копии структур: изменения, сделанные сервером, видны только в возвращённом значении.

Чтение из JSON и запись в JSON

ПрочитатьJSON возвращает JSON-массив объектов массивом структур, а со вторым параметром Истина — массивом соответствий. Ответ сервиса удобно нормализовать: читать в соответствия, потому что ключи JSON бывают любыми, переименовать поля и подставить умолчания, где поля нет или пришёл null. ЗаписатьJSON принимает такой массив напрямую, но ссылку и Null не запишет — их надо заменить. Функции добавьте в модуль МассивСтруктурСервер; даты, кодировки и ошибки разбора — в статье про JSON в 1С.

// Добавьте в общий модуль МассивСтруктурСервер. Платформа 8.3.6 и новее.

// JSON-массив объектов -> массив структур с одинаковым набором полей.
// Поля       - Структура: ключ - имя поля в 1С, значение - значение по умолчанию
//              на случай, если поля нет в объекте JSON или там null.
// ИменаВJSON - Соответствие: имя поля в 1С -> ключ в JSON, если они различаются.
Функция МассивСтруктурИзJSON(ТекстJSON, Поля, ИменаВJSON = Неопределено) Экспорт

	Чтение = Новый ЧтениеJSON;
	Чтение.УстановитьСтроку(ТекстJSON);
	Данные = ПрочитатьJSON(Чтение, Истина); // в соответствия: годятся любые ключи
	Чтение.Закрыть();

	Если ТипЗнч(Данные) <> Тип("Массив") Тогда
		ВызватьИсключение "Ожидался JSON-массив объектов";
	КонецЕсли;

	Результат = Новый Массив;
	Для Каждого ОбъектJSON Из Данные Цикл
		Если ТипЗнч(ОбъектJSON) <> Тип("Соответствие") Тогда
			Продолжить; // элемент массива - не объект, а число или строка
		КонецЕсли;
		Элемент = Новый Структура;
		Для Каждого Поле Из Поля Цикл
			КлючJSON = Поле.Ключ;
			Если ИменаВJSON <> Неопределено И ИменаВJSON.Получить(Поле.Ключ) <> Неопределено Тогда
				КлючJSON = ИменаВJSON.Получить(Поле.Ключ);
			КонецЕсли;
			Значение = ОбъектJSON.Получить(КлючJSON); // нет ключа или null - Неопределено
			Элемент.Вставить(Поле.Ключ, ?(Значение = Неопределено, Поле.Значение, Значение));
		КонецЦикла;
		Результат.Добавить(Элемент);
	КонецЦикла;

	Возврат Результат;

КонецФункции

// Массив структур -> компактная строка JSON.
Функция МассивСтруктурВJSON(Массив) Экспорт

	Запись = Новый ЗаписьJSON;
	Запись.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет));
	ЗаписатьJSON(Запись, ЗначениеДляJSON(Массив));

	Возврат Запись.Закрыть();

КонецФункции

// Пример: позиции из ответа сервиса -> свёртка по артикулу -> JSON.
Функция ПримерОбменаJSON() Экспорт

	ТекстJSON = "[{""sku"": ""A-100"", ""qty"": 2, ""price"": 150},
	|{""sku"": ""B-205"", ""qty"": null},
	|{""sku"": ""A-100"", ""qty"": 3, ""price"": 150}]";

	Поля = Новый Структура("Артикул, Количество, Цена", "", 0, 0);
	ИменаВJSON = Новый Соответствие;
	ИменаВJSON.Вставить("Артикул", "sku");
	ИменаВJSON.Вставить("Количество", "qty");
	ИменаВJSON.Вставить("Цена", "price");

	Позиции = МассивСтруктурИзJSON(ТекстJSON, Поля, ИменаВJSON);
	// У B-205 Количество = 0 (пришёл null) и Цена = 0 (поля не было)

	Итоги = МассивСтруктурКлиентСервер.Свернуть(Позиции, "Артикул", "Количество");

	// Два объекта: A-100 с количеством 5 и B-205 с количеством 0
	Возврат МассивСтруктурВJSON(Итоги);

КонецФункции

// Копия коллекции, которую примет ЗаписатьJSON: Null -> null,
// ссылки и уникальные идентификаторы -> строка, пустая ссылка -> null.
Функция ЗначениеДляJSON(Значение)

	ТипЗначения = ТипЗнч(Значение);

	Если ТипЗначения = Тип("Массив") Или ТипЗначения = Тип("ФиксированныйМассив") Тогда
		Результат = Новый Массив;
		Для Каждого Элемент Из Значение Цикл
			Результат.Добавить(ЗначениеДляJSON(Элемент));
		КонецЦикла;
		Возврат Результат;
	КонецЕсли;

	Если ТипЗначения = Тип("Структура") Или ТипЗначения = Тип("ФиксированнаяСтруктура") Тогда
		Результат = Новый Структура;
		Для Каждого КлючИЗначение Из Значение Цикл
			Результат.Вставить(КлючИЗначение.Ключ, ЗначениеДляJSON(КлючИЗначение.Значение));
		КонецЦикла;
		Возврат Результат;
	КонецЕсли;

	Если ТипЗначения = Тип("Соответствие") Или ТипЗначения = Тип("ФиксированноеСоответствие") Тогда
		Результат = Новый Соответствие;
		Для Каждого КлючИЗначение Из Значение Цикл
			// Ключ объекта JSON - только строка
			Ключ = КлючИЗначение.Ключ;
			Если ТипЗнч(Ключ) <> Тип("Строка") Тогда
				Ключ = XMLСтрока(Ключ);
			КонецЕсли;
			Результат.Вставить(Ключ, ЗначениеДляJSON(КлючИЗначение.Значение));
		КонецЦикла;
		Возврат Результат;
	КонецЕсли;

	Если Значение = Null Или Значение = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;

	Если ТипЗначения = Тип("Строка") Или ТипЗначения = Тип("Число")
		Или ТипЗначения = Тип("Булево") Или ТипЗначения = Тип("Дата") Тогда
		Возврат Значение;
	КонецЕсли;

	// Ссылки и УникальныйИдентификатор: у справочников и документов - GUID,
	// у перечислений - имя значения; пустая ссылка - null
	Если Не ЗначениеЗаполнено(Значение) Тогда
		Возврат Неопределено;
	КонецЕсли;

	Возврат XMLСтрока(Значение);

КонецФункции

После нормализации у всех элементов одинаковые поля, и работают точка и свёртка. Ключи итогового JSON — имена полей структур: если сервис ждёт sku, так и называйте поле. Ссылки уходят строкой — GUID у справочников и документов, имя значения у перечислений. Обратно ссылку даёт метод менеджера: Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрокаGUID)).

Когда нужен массив соответствий

Массив соответствий — тот же список, где каждая запись — соответствие. Он нужен, когда:

  • ключи не годятся в имена: order-id, 2024, price.old. Структура на таком ключе даёт ошибку «Задано неправильное имя атрибута структуры», соответствие примет любую строку;
  • важен регистр: структура не различает "id" и "ID", соответствие различает;
  • состав полей неизвестен: Элемент["discount"] для отсутствующего ключа вернёт Неопределено, а не ошибку.

Плата — синтаксис: точка не работает, а код под структуры падает: Элемент.Свойство("sku") даёт «Метод объекта не обнаружен (Свойство)». Поэтому массив соответствий хорош как слой между JSON и кодом, а внутри конфигурации удобнее массив структур. Функции МассивСтруктурКлиентСервер принимают оба варианта, правила передачи между клиентом и сервером те же.

Типичные ошибки

  • Все элементы одинаковые. Структуру создали до цикла: в массиве несколько ссылок на один объект с данными последней строки. Создавайте её внутри цикла.
  • «Поле объекта не обнаружено (Цена)». У части элементов нет поля — обычно в данных из JSON. Читайте через Свойство() или ЗначениеПоля, а ответ сервиса нормализуйте функцией МассивСтруктурИзJSON.
  • «Тип не определен (ТаблицаЗначений)». Сортировку через таблицу вызвали в процедуре &НаКлиенте. Перенесите вызов на сервер или используйте СортироватьПоПолю.
  • «Операции сравнения на больше-меньше допустимы только для значений совпадающих примитивных типов (Булево, Число, Строка, Дата)». СортироватьПоПолю встретила ссылку, Неопределено или смесь типов. Такие поля сортируйте на сервере функцией Отсортировать.
  • Найти() возвращает Неопределено. Позиции.Найти(Новый Структура("Артикул", "A-100")) ищет тот же самый объект, а не такое же содержимое. Нужен НайтиПоПолю.
  • Правки на сервере «потерялись». Сервер получил копию массива, а с Знач она обратно не возвращается. Возвращайте результат из функции.

Частые вопросы

Как отсортировать массив структур по двум полям?

Серверной функцией Отсортировать: МассивСтруктурСервер.Отсортировать(Позиции, "Склад, Сумма Убыв"). Направление пишут после имени поля, как в методе Сортировать таблицы значений; без него сортировка идёт по возрастанию.

Как превратить массив структур в таблицу значений и обратно?

Из таблицы в массив — функцией БСП ОбщегоНазначения.ТаблицаЗначенийВМассив, а для данных из запроса — функцией ИзРезультатаЗапроса выше, без промежуточной таблицы. Обратное преобразование с типизацией колонок — в статье массив в таблицу значений.

Можно ли передать массив структур параметром в запрос?

Нет. Условие В (&Список) принимает массив простых значений или ссылок: выгрузите нужное поле функцией ВыгрузитьПоле. Если в запросе нужны несколько полей сразу, например артикул и количество для соединения с остатками, переложите массив в таблицу значений с типизированными колонками.

Как скопировать массив структур, чтобы правки не затронули оригинал?

Цикл с Добавить() копирует только массив, структуры внутри остаются общими. Нужна рекурсивная копия: в БСП это ОбщегоНазначения.СкопироватьРекурсивно на сервере и ОбщегоНазначенияКлиент.СкопироватьРекурсивно на клиенте, без БСП — функция КопияКоллекции из статьи про структуру 1С.

Когда проще заказать

Такой массив обычно лишь транспорт внутри большой задачи: загрузки из файла, обмена с внешним сервисом, обработки выделенных строк документа. Если нужна готовая обработка или отчёт, а не разбор коллекций, посмотрите услугу отчёты и обработки 1С или разместите задачу на площадке: оценка бесплатная, ответ в течение рабочего дня.

По теме

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.


1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.