Склейка двух и более массивов во встроенном языке 1С 8.3: от цикла до функций БСП
Содержание
Чтобы объединить массивы в 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, второй массив окажется в начале в обратном порядке. Это частая ошибка при переделке кода «на скорую руку».
Готовая процедура БСП для объединения массивов
В типовых конфигурациях на БСП не нужно писать цикл. В общем модуле ОбщегоНазначенияКлиентСервер есть процедура:
ОбщегоНазначенияКлиентСервер.ДополнитьМассив(МассивПриемник, МассивИсточник, ТолькоУникальныеЗначения);
Как она работает:
- Берёт каждый элемент
МассивИсточники добавляет его вМассивПриемник. - Если третий параметр равен
Истина, пропускает элементы, которые в приёмнике уже есть. - Ничего не возвращает: меняется сам приёмник.
По умолчанию третий параметр Ложь, и дубли попадают в результат. Модуль клиент-серверный, процедуру вызывают и в форме, и в модуле объекта.
Там же есть ещё две функции, которые пригодятся рядом:
| Функция | Что делает |
|---|---|
СвернутьМассив(Массив) | возвращает новый массив без повторяющихся значений |
РазностьМассивов(Массив, ВычитаемыйМассив) | возвращает элементы первого массива, которых нет во втором |
Перед использованием откройте модуль ОбщегоНазначенияКлиентСервер в конфигураторе и проверьте сигнатуру поиском по имени. В собственных и сильно доработанных конфигурациях БСП может быть старой или урезанной.
Как объединить списки значений, таблицы и фиксированные массивы
СписокЗначений
У списка есть пара методов для обмена с массивом:
ЗагрузитьЗначения(Массив)— заполняет список значениями массива. Старое содержимое списка при этом удаляется, поэтому для объединения этот метод в одиночку не подходит.ВыгрузитьЗначения()— возвращает массив значений списка.
Объединить два списка с сохранением представлений и пометок можно так:
Для Каждого ЭлементСписка Из Список2 Цикл
Список1.Добавить(ЭлементСписка.Значение, ЭлементСписка.Представление, ЭлементСписка.Пометка);
КонецЦикла;
Если представления не нужны, короче выгрузить оба списка в массивы, склеить их и загрузить результат обратно через ЗагрузитьЗначения().
Список значений полезен ещё и тем, что умеет сортировать: у массива сортировки нет, а у списка есть СортироватьПоЗначению(НаправлениеСортировки.Возр). Нужен отсортированный объединённый массив — загрузите его в список, отсортируйте и выгрузите.
ТаблицаЗначений
Колонку таблицы превращает в массив метод ВыгрузитьКолонку("ИмяКолонки"). Две таблицы с одинаковыми колонками объединяют построчно:
Для Каждого Строка Из Таблица2 Цикл
ЗаполнитьЗначенияСвойств(Таблица1.Добавить(), Строка);
КонецЦикла;
ЗаполнитьЗначенияСвойств переносит значения колонок с совпадающими именами. Колонки, которых нет в первой таблице, просто не попадут в результат.
ФиксированныйМассив
Фиксированный массив изменить нельзя, у него нет Добавить(). Порядок такой:
- Сделать обычную копию:
Рабочий = Новый Массив(Фиксированный); - Дописать в неё второй массив циклом.
- Если результат должен остаться неизменяемым, обернуть обратно:
Итог = Новый ФиксированныйМассив(Рабочий);
Фиксированные массивы часто приходят из параметров сеанса и реквизитов форм, поэтому ошибка «Метод объекта не обнаружен (Добавить)» на склейке почти всегда означает именно этот случай.
Объединение массивов без дубликатов
Выбор способа зависит от объёма и от того, важен ли порядок элементов.
| Способ | Когда подходит | Минус |
|---|---|---|
Найти() перед Добавить() | десятки и сотни элементов | каждый поиск перебирает весь результат, на больших объёмах медленно |
Соответствие как множество | любые объёмы, порядок важен | на несколько строк кода больше |
ТаблицаЗначений.Свернуть() | данные уже в таблице | порядок первого появления не гарантируется |
ДополнитьМассив(…, Истина) или СвернутьМассив() | конфигурация на БСП | зависимость от библиотеки |
Самый быстрый вариант — через Соответствие
Поиск ключа в соответствии не перебирает элементы по одному, поэтому этот способ выдерживает десятки тысяч значений:
Функция ОбъединитьУникальные(Источники)
Встречались = Новый Соответствие;
Результат = Новый Массив;
Для Каждого Источник Из Источники Цикл
Для Каждого Элемент Из Источник Цикл
Если Встречались[Элемент] = Неопределено Тогда
Встречались.Вставить(Элемент, Истина);
Результат.Добавить(Элемент);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Возврат Результат;
КонецФункции
Порядок сохраняется: каждый элемент остаётся там, где встретился впервые.
Через таблицу значений
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Значение");
Для Каждого Элемент Из Массив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 позиций так и остались пустыми. Используйте либо запись по индексу, либо пустой конструктор.Как объединить две структуры?
Для Каждого и вызвать Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение) у первой. Совпадающие ключи перезапишутся значениями второй структуры.Можно ли передать объединённый массив в запрос?
Запрос.УстановитьПараметр("Список", Результат) и условие ГДЕ Ссылка В (&Список). Для очень больших массивов надёжнее загрузить их во временную таблицу через таблицу значений.