Склейка двух и более массивов во встроенном языке 1С 8.3: от цикла до функций БСП

· 10 мин

Содержание

Чтобы объединить массивы в 1С, нужно перебрать второй массив циклом Для Каждого и добавить каждый элемент в первый методом Добавить(). Если в конфигурации есть БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11 и почти все типовые), то же самое делает одна строка: ОбщегоНазначенияКлиентСервер.ДополнитьМассив(Приемник, Источник).

Частое заблуждение — искать у массива метод или свойство «Соединить». У объекта Массив в платформе 1С:Предприятие 8.3 такого нет. Есть глобальная функция СтрСоединить(), но она склеивает элементы в одну строку, а не два массива в один. Всё, что написано ниже, общее для платформы и работает в любой конфигурации, кроме мест, где явно упомянута БСП.

Почему у массива в 1С нет метода «Соединить»

Объект Массив умеет немного: Добавить(), Вставить(), Удалить(), Найти(), Количество(), ВГраница(), Получить(), Установить(), Очистить(). Готовой операции «приклеить другой массив» в этом списке нет, поэтому склейку пишут сами или берут из БСП.

Путаница с «Соединить» идёт от двух функций для строк:

  • 🔗 СтрСоединить(Массив, ", ") — превращает массив строк в одну строку через разделитель. Элементы должны быть строками, числа сначала переводят через Формат().
  • ✂️ СтрРазделить("А;Б;В", ";") — обратная операция, из строки делает массив.

Обе функции появились в платформе 8.3.6. Для объединения коллекций они не нужны, а вот для вывода результата в сообщение удобны:

Сообщить(СтрСоединить(Результат, "; "));

Объединение массивов через цикл и метод Добавить

Базовый способ работает в любой версии 8.3, на клиенте и на сервере, без зависимостей.

Дописать второй массив в конец первого

Для Каждого Элемент Из Массив2 Цикл
	Массив1.Добавить(Элемент);
КонецЦикла;

После выполнения Массив1 содержит свои элементы, а за ними все элементы Массив2 в исходном порядке. Сам Массив2 не меняется.

Получить новый массив, не трогая исходные

Если первый массив нужен дальше в прежнем виде, собирайте результат в отдельную коллекцию:

Функция ОбъединитьМассивы(Массив1, Массив2)

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

	Для Каждого Элемент Из Массив1 Цикл
		Результат.Добавить(Элемент);
	КонецЦикла;

	Для Каждого Элемент Из Массив2 Цикл
		Результат.Добавить(Элемент);
	КонецЦикла;

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

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

Первый цикл можно заменить конструктором копии. У массива есть конструктор по фиксированному массиву, поэтому копия делается в одну строку:

Результат = Новый Массив(Новый ФиксированныйМассив(Массив1));

Склеить сразу несколько массивов

Когда источников три и больше, передайте их одним параметром — массивом массивов:

Функция ОбъединитьНесколько(Источники)

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

	Для Каждого Источник Из Источники Цикл
		Для Каждого Элемент Из Источник Цикл
			Результат.Добавить(Элемент);
		КонецЦикла;
	КонецЦикла;

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

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

// Вызов
Источники = Новый Массив;
Источники.Добавить(МассивОрганизаций);
Источники.Добавить(МассивФилиалов);
Источники.Добавить(МассивОбособленных);
Все = ОбъединитьНесколько(Источники);

Вставить второй массив в начало

Метод Вставить(Индекс, Значение) кладёт элемент на указанную позицию. Чтобы порядок второго массива сохранился, вставляйте с растущим индексом:

Для Индекс = 0 По Массив2.ВГраница() Цикл
	Массив1.Вставить(Индекс, Массив2[Индекс]);
КонецЦикла;

Если вставлять всё в позицию 0, второй массив окажется в начале в обратном порядке. Это частая ошибка при переделке кода «на скорую руку».

Готовая процедура БСП для объединения массивов

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

ОбщегоНазначенияКлиентСервер.ДополнитьМассив(МассивПриемник, МассивИсточник, ТолькоУникальныеЗначения);

Как она работает:

  1. Берёт каждый элемент МассивИсточник и добавляет его в МассивПриемник.
  2. Если третий параметр равен Истина, пропускает элементы, которые в приёмнике уже есть.
  3. Ничего не возвращает: меняется сам приёмник.

По умолчанию третий параметр Ложь, и дубли попадают в результат. Модуль клиент-серверный, процедуру вызывают и в форме, и в модуле объекта.

Там же есть ещё две функции, которые пригодятся рядом:

ФункцияЧто делает
СвернутьМассив(Массив)возвращает новый массив без повторяющихся значений
РазностьМассивов(Массив, ВычитаемыйМассив)возвращает элементы первого массива, которых нет во втором

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

Как объединить списки значений, таблицы и фиксированные массивы

СписокЗначений

У списка есть пара методов для обмена с массивом:

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

Объединить два списка с сохранением представлений и пометок можно так:

Для Каждого ЭлементСписка Из Список2 Цикл
	Список1.Добавить(ЭлементСписка.Значение, ЭлементСписка.Представление, ЭлементСписка.Пометка);
КонецЦикла;

Если представления не нужны, короче выгрузить оба списка в массивы, склеить их и загрузить результат обратно через ЗагрузитьЗначения().

Список значений полезен ещё и тем, что умеет сортировать: у массива сортировки нет, а у списка есть СортироватьПоЗначению(НаправлениеСортировки.Возр). Нужен отсортированный объединённый массив — загрузите его в список, отсортируйте и выгрузите.

ТаблицаЗначений

Колонку таблицы превращает в массив метод ВыгрузитьКолонку("ИмяКолонки"). Две таблицы с одинаковыми колонками объединяют построчно:

Для Каждого Строка Из Таблица2 Цикл
	ЗаполнитьЗначенияСвойств(Таблица1.Добавить(), Строка);
КонецЦикла;

ЗаполнитьЗначенияСвойств переносит значения колонок с совпадающими именами. Колонки, которых нет в первой таблице, просто не попадут в результат.

ФиксированныйМассив

Фиксированный массив изменить нельзя, у него нет Добавить(). Порядок такой:

  1. Сделать обычную копию: Рабочий = Новый Массив(Фиксированный);
  2. Дописать в неё второй массив циклом.
  3. Если результат должен остаться неизменяемым, обернуть обратно: Итог = Новый ФиксированныйМассив(Рабочий);

Фиксированные массивы часто приходят из параметров сеанса и реквизитов форм, поэтому ошибка «Метод объекта не обнаружен (Добавить)» на склейке почти всегда означает именно этот случай.

Объединение массивов без дубликатов

Выбор способа зависит от объёма и от того, важен ли порядок элементов.

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

Самый быстрый вариант — через Соответствие

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

Функция ОбъединитьУникальные(Источники)

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

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

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

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

Порядок сохраняется: каждый элемент остаётся там, где встретился впервые.

Через таблицу значений

Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Значение");

Для Каждого Элемент Из Массив1 Цикл
	Таблица.Добавить().Значение = Элемент;
КонецЦикла;
Для Каждого Элемент Из Массив2 Цикл
	Таблица.Добавить().Значение = Элемент;
КонецЦикла;

Таблица.Свернуть("Значение");
Результат = Таблица.ВыгрузитьКолонку("Значение");

Что считается дублем

Платформа сравнивает значения строго:

  • 🔤 Строки сравниваются с учётом регистра и пробелов: "ООО Ромашка" и "ООО Ромашка " — разные значения. Нормализуйте перед сравнением, например ВРег(СокрЛП(Элемент)).
  • 🔢 Числа 5 и строка "5" — разные значения разных типов.
  • 📎 Ссылки на элементы справочников и документы сравниваются корректно: одна и та же ссылка, полученная двумя запросами, считается дублем.
  • 📦 Структуры, массивы и другие объекты сравниваются как объекты: две структуры с одинаковыми полями — это два разных значения. Для таких данных ищите дубли по ключевому полю.
Какой способ объединения выбрать?

Копия или ссылка: что на самом деле лежит в объединённом массиве

Массив во встроенном языке — объект. Переменная хранит не сам массив, а ссылку на него. Отсюда три следствия, которые ловят даже опытных разработчиков.

1. Присваивание не копирует. После Массив3 = Массив1; обе переменные указывают на один массив. Склейка Массив3.Добавить(…) изменит и Массив1.

2. Цикл с Добавить() копирует только верхний уровень. Если в массиве лежат структуры, новый массив получит ссылки на те же структуры. Изменили поле в результате — оно изменилось и в источнике. Это поверхностная (ссылочная) копия.

3. Знач в параметре не защищает содержимое. Процедура с параметром Знач Массив не может подменить переменную вызывающего кода, но методом Добавить() спокойно изменит сам массив.

Для примитивов (строки, числа, даты, булево) и ссылок на объекты базы поверхностной копии достаточно: эти значения не меняются «изнутри». Глубокая копия нужна, когда элементы — структуры, соответствия или вложенные массивы.

Глубокое копирование своей функцией

Функция КопияГлубокая(Значение)

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

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

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

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

	Иначе
		Результат = Значение;
	КонецЕсли;

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

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

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

Объединение с глубокой копией: Результат = КопияГлубокая(ОбъединитьМассивы(Массив1, Массив2));.

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

Вызов сервера из клиента — отдельная история. Массив уходит на сервер сериализованной копией, а параметр без Знач после вызова возвращается на клиент вместе с изменениями. С Знач изменения, сделанные на сервере, на клиент не вернутся.

Скорость объединения больших массивов

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

  • 🐢 Найти() внутри цикла. Каждый вызов перебирает массив с начала. Склейка двух массивов по 50 000 элементов с проверкой дублей через Найти() даёт порядка миллиардов сравнений. Замена на Соответствие убирает эту проблему.
  • 🐢 Вставить(0, …) в цикле. Каждая вставка в начало сдвигает весь массив. Нужен обратный порядок — соберите массив через Добавить() и перепишите его с конца.
  • 🐢 Передача массивов между клиентом и сервером. Каждый серверный вызов сериализует параметры. Склеивайте большие массивы там, где они получены: если данные пришли из запроса на сервере, там же и объединяйте, а на клиент отдавайте готовый результат.
  • 🐢 Склейка результатов запросов в коде. Если оба массива — выгрузки из базы, объединение быстрее сделать в самом запросе.

Объединение в запросе вместо кода

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Контрагенты.Ссылка КАК Ссылка
|ИЗ
|	Справочник.Контрагенты КАК Контрагенты
|ГДЕ
|	Контрагенты.ЭтоГруппа = ЛОЖЬ
|	И Контрагенты.ПометкаУдаления = ЛОЖЬ
|
|ОБЪЕДИНИТЬ
|
|ВЫБРАТЬ
|	РеализацияТоваровУслуг.Контрагент
|ИЗ
|	Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
|ГДЕ
|	РеализацияТоваровУслуг.Дата >= &НачалоПериода";

Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(ТекущаяДатаСеанса()));
Результат = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

ОБЪЕДИНИТЬ сразу убирает повторяющиеся строки, ОБЪЕДИНИТЬ ВСЕ оставляет все. Имя поля в результате берётся из первого запроса объединения, поэтому псевдоним КАК Ссылка достаточно задать в первом блоке. Имена реквизитов в примере — из типовой Бухгалтерии 3.0, в других конфигурациях подставьте свои.

Если размер известен заранее

Конструктор Новый Массив(Количество) создаёт массив нужной длины, заполненный значениями Неопределено. Элементы затем записывают по индексу:

Количество1 = Массив1.Количество();
Результат = Новый Массив(Количество1 + Массив2.Количество());

Для Индекс = 0 По Массив1.ВГраница() Цикл
	Результат[Индекс] = Массив1[Индекс];
КонецЦикла;

Для Индекс = 0 По Массив2.ВГраница() Цикл
	Результат[Количество1 + Индекс] = Массив2[Индекс];
КонецЦикла;

Для двух массивов по 3 и 4 элемента получится массив из 7: индексы 0–2 займут элементы первого, индексы 3–6 — второго. Выигрыш перед Добавить() скромный, но такой код нагляднее, когда позиции элементов важны.

Частые вопросы об объединении массивов в 1С

Как найти общие элементы двух массивов?
Положите элементы первого массива в Соответствие как ключи, затем переберите второй и добавляйте в результат те, что есть в соответствии. Это пересечение работает быстро и на больших объёмах.
Можно ли объединить массивы разной размерности?
Да. Если элементы второго массива — тоже массивы, цикл с Добавить() положит их целиком как вложенные. Чтобы «распрямить» структуру, нужен вложенный цикл, который добавляет элементы внутренних массивов.
Почему после объединения в массиве появились значения Неопределено?
Чаще всего массив создан конструктором Новый Массив(N), а затем в него дописали элементы через Добавить(). Первые N позиций так и остались пустыми. Используйте либо запись по индексу, либо пустой конструктор.
Как объединить две структуры?
Перебрать вторую циклом Для Каждого и вызвать Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение) у первой. Совпадающие ключи перезапишутся значениями второй структуры.
Можно ли передать объединённый массив в запрос?
Да, как параметр: Запрос.УстановитьПараметр("Список", Результат) и условие ГДЕ Ссылка В (&Список). Для очень больших массивов надёжнее загрузить их во временную таблицу через таблицу значений.