Слияние двух массивов на встроенном языке 1С: от простого цикла до запроса
Содержание
Встроенного метода «склеить два массива» у объекта Массив в 1С нет. Объединить 2 массива можно циклом Для Каждого с методом Добавить(), а в конфигурациях на БСП — одной строкой ОбщегоНазначенияКлиентСервер.ДополнитьМассив(МассивА, МассивБ). Если нужны только уникальные значения, третий параметр этой процедуры ставят в Истина или убирают дубли через Соответствие.
Выбор способа зависит от трёх вещей: нужны ли дубли, важен ли порядок и сколько элементов в массивах. Для сотни строк хватит цикла, для десятков тысяч ссылок лучше Соответствие или запрос с ОБЪЕДИНИТЬ. Код ниже — встроенный язык платформы 8.3, без привязки к конкретной конфигурации, кроме отдельно отмеченных процедур БСП.
Что нужно знать о массивах до слияния
Три свойства массива определяют, какой способ объединения выбрать и где можно ошибиться.
- 🔢 Индексы с нуля. Первый элемент —
Массив[0], последний —Массив[Массив.Количество() - 1]. МетодВГраница()у пустого массива возвращает-1. - 🔗 Передача по ссылке. Массив — объект. Строка
МассивВ = МассивАне создаёт копию: обе переменные смотрят на один и тот же массив. Добавили элемент вМассивВ— он появился и вМассивА. - 🧩 Любые типы внутри. В одном массиве могут лежать строка, число, ссылка и структура. Для сравнения при уникализации это важно: структуры и таблицы сравниваются по ссылке на объект, а не по содержимому.
Отсюда первое правило: если исходные массивы ещё понадобятся, объединяйте в новый массив, а не дописывайте второй в первый.
Копию массива в одну строку делают через фиксированный массив:
КопияА = Новый Массив(Новый ФиксированныйМассив(МассивА));
Копия поверхностная: если внутри лежат структуры, обе копии будут ссылаться на одни и те же структуры.
Объединение двух массивов циклом
Базовый способ работает везде: на сервере, на тонком клиенте, во внешней обработке, в конфигурации без БСП.
Функция ОбъединитьМассивы(МассивА, МассивБ)
Результат = Новый Массив;
Для Каждого Элемент Из МассивА Цикл
Результат.Добавить(Элемент);
КонецЦикла;
Для Каждого Элемент Из МассивБ Цикл
Результат.Добавить(Элемент);
КонецЦикла;
Возврат Результат;
КонецФункции
Порядок сохраняется: сначала все элементы первого массива, потом второго. Дубли остаются — если значение есть в обоих массивах, оно попадёт в результат дважды.
Функцию удобно положить в общий модуль с флажками «Клиент» и «Сервер», чтобы вызывать её из любого контекста.
Дописать второй массив в первый
Если исходный МассивА больше не нужен, создавать третий массив незачем:
Для Каждого Элемент Из МассивБ Цикл
МассивА.Добавить(Элемент);
КонецЦикла;
Не дописывайте массив сам в себя циклом Для Каждого Элемент Из МассивА с МассивА.Добавить(). Коллекция растёт по ходу обхода, и результат будет не тот, что вы ждёте. Сначала сделайте копию.
Готовая процедура из БСП
В конфигурациях на Библиотеке стандартных подсистем (Бухгалтерия 3.0, ЗУП 3.1, УТ 11, ERP и другие) есть процедура общего модуля:
ОбщегоНазначенияКлиентСервер.ДополнитьМассив(МассивА, МассивБ);
// уникальные значения:
ОбщегоНазначенияКлиентСервер.ДополнитьМассив(МассивА, МассивБ, Истина);
Это процедура, а не функция: она ничего не возвращает и меняет первый массив (приёмник). Второй массив остаётся как был. Во внешней обработке для своей базы на БСП её можно вызывать напрямую, для чужих баз надёжнее свой цикл.
Вставка элементов: Добавить и Вставить
Метод Добавить(Значение) ставит элемент в конец. Метод Вставить(Индекс, Значение) — на позицию с указанным индексом, сдвигая остальные вправо.
| Метод | Куда ставит | Когда использовать |
|---|---|---|
Добавить(Значение) | В конец | Обычное слияние «первый + второй» |
Вставить(0, Значение) | В начало | Второй массив должен идти перед первым |
Вставить(Индекс, Значение) | На позицию | Вклеить массив в середину |
Установить(Индекс, Значение) | Заменяет элемент | Заполнение заранее созданного массива |
Вставить второй массив в начало первого
Если вставлять элементы по одному в позицию 0, порядок перевернётся. Правильно — сдвигать позицию вставки:
Позиция = 0;
Для Каждого Элемент Из МассивБ Цикл
МассивА.Вставить(Позиция, Элемент);
Позиция = Позиция + 1;
КонецЦикла;
Было: МассивА = [1, 2], МассивБ = [8, 9]. Стало: [8, 9, 1, 2].
Вклеить массив в середину
Тот же приём, только стартовая позиция другая. Вставить МассивБ после второго элемента МассивА:
Позиция = 2;
Для Каждого Элемент Из МассивБ Цикл
МассивА.Вставить(Позиция, Элемент);
Позиция = Позиция + 1;
КонецЦикла;
Индекс вставки допустим от 0 до МассивА.Количество() включительно. Значение Количество() равносильно добавлению в конец.
Чередование элементов
Иногда нужно не склеить, а перемешать по порядку: первый из А, первый из Б, второй из А и так далее. Длина массивов может отличаться:
Результат = Новый Массив;
Макс = Макс(МассивА.Количество(), МассивБ.Количество());
Для Индекс = 0 По Макс - 1 Цикл
Если Индекс < МассивА.Количество() Тогда
Результат.Добавить(МассивА[Индекс]);
КонецЕсли;
Если Индекс < МассивБ.Количество() Тогда
Результат.Добавить(МассивБ[Индекс]);
КонецЕсли;
КонецЦикла;
Если оба массива пустые, Макс - 1 равно -1, цикл не выполнится ни разу, ошибки не будет.
Как объединить массивы без повторов
Самый частый запрос на практике — объединить два списка ссылок (номенклатура, контрагенты, документы) так, чтобы каждая ссылка встретилась один раз.
Медленный способ: Найти в цикле
Результат = Новый Массив(Новый ФиксированныйМассив(МассивА));
Для Каждого Элемент Из МассивБ Цикл
Если Результат.Найти(Элемент) = Неопределено Тогда
Результат.Добавить(Элемент);
КонецЕсли;
КонецЦикла;
Метод Найти() перебирает массив от начала до конца. Для массивов по 50–100 элементов разницы не заметно. Но при 20 000 элементах в каждом это уже до 400 млн сравнений, и код будет работать минуты.
Кроме того, этот вариант не чистит дубли внутри самого МассивА — если там они были, они останутся.
Быстрый способ: Соответствие
Ключи Соответствия уникальны, а поиск по ключу не перебирает коллекцию целиком. Это стандартный приём для уникализации:
Функция ОбъединитьБезПовторов(МассивА, МассивБ)
Встречено = Новый Соответствие;
Результат = Новый Массив;
Для Каждого Элемент Из МассивА Цикл
Если Встречено[Элемент] = Неопределено Тогда
Встречено.Вставить(Элемент, Истина);
Результат.Добавить(Элемент);
КонецЕсли;
КонецЦикла;
Для Каждого Элемент Из МассивБ Цикл
Если Встречено[Элемент] = Неопределено Тогда
Встречено.Вставить(Элемент, Истина);
Результат.Добавить(Элемент);
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Порядок первого появления сохраняется, дубли убираются и между массивами, и внутри каждого из них.
💡 В БСП есть готовая ОбщегоНазначенияКлиентСервер.СвернутьМассив(Массив) — она возвращает массив без повторов. Сначала склеиваете циклом, потом сворачиваете.
Строки в разном регистре
Для массива и соответствия "ООО Ромашка" и "ооо ромашка" — разные значения. Если строки пришли из разных источников (Excel, ввод пользователя), нормализуйте ключ:
Ключ = ВРег(СокрЛП(Элемент));
Если Встречено[Ключ] = Неопределено Тогда
Встречено.Вставить(Ключ, Истина);
Результат.Добавить(Элемент);
КонецЕсли;
В результат попадает исходная строка, а сравнение идёт по очищенному ключу.
Массивы структур: уникальность по полю
Две структуры с одинаковым содержимым — разные объекты, Найти() их не сочтёт дублями. Уникализируйте по ключевому полю:
Для Каждого Строка Из МассивБ Цикл
Если Встречено[Строка.Код] = Неопределено Тогда
Встречено.Вставить(Строка.Код, Истина);
Результат.Добавить(Строка);
КонецЕсли;
КонецЦикла;
Таблица значений как промежуточный контейнер
Когда кроме объединения нужно свернуть данные с суммированием, отсортировать или сгруппировать, массив неудобен. Тогда оба массива перекладывают в таблицу значений.
Пример: два массива структур Номенклатура + Количество из разных документов, нужно получить итог по каждой позиции.
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура");
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
Для Каждого Источник Из МассивА Цикл
ЗаполнитьЗначенияСвойств(ТЗ.Добавить(), Источник);
КонецЦикла;
Для Каждого Источник Из МассивБ Цикл
ЗаполнитьЗначенияСвойств(ТЗ.Добавить(), Источник);
КонецЦикла;
ТЗ.Свернуть("Номенклатура", "Количество");
ТЗ.Сортировать("Номенклатура");
После Свернуть() в таблице одна строка на каждую номенклатуру, количество просуммировано.
Таблица значений пригодится и для массивов простых значений. Получить уникальный отсортированный массив можно так:
ТЗ.Свернуть("Значение");
ТЗ.Сортировать("Значение");
Результат = ТЗ.ВыгрузитьКолонку("Значение");
ТаблицаЗначений недоступна на тонком клиенте. В модуле управляемой формы этот код пишите в процедуре с директивой &НаСервере, иначе получите ошибку о неизвестном типе.
У самого массива метода сортировки нет. Для сортировки без таблицы используют СписокЗначений: ЗагрузитьЗначения(Массив), затем СортироватьПоЗначению() и ВыгрузитьЗначения().
Объединение массивов в запросе
Если элементы — ссылки на объекты базы, объединение и уникализацию можно отдать СУБД. Это быстрее циклов на больших объёмах и сразу даёт сортировку и доступ к реквизитам.
Вариант 1: два параметра и условие В
Самый короткий путь для массивов ссылок одного типа:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| (Номенклатура.Ссылка В (&МассивА)
| ИЛИ Номенклатура.Ссылка В (&МассивБ))
|
|УПОРЯДОЧИТЬ ПО
| Номенклатура.Наименование";
Запрос.УстановитьПараметр("МассивА", МассивА);
Запрос.УстановитьПараметр("МассивБ", МассивБ);
Результат = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
Дублей не будет: каждая запись справочника выбирается один раз. Заменив ИЛИ на И, получите пересечение — элементы, которые есть в обоих массивах.
Вариант 2: ОБЪЕДИНИТЬ через временные таблицы
Для массивов строк, чисел или ссылок разных типов массивы сначала превращают в таблицы значений с типизированной колонкой, а затем помещают во временные таблицы:
ОписаниеСтроки = Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150));
ТаблицаА = Новый ТаблицаЗначений;
ТаблицаА.Колонки.Добавить("Значение", ОписаниеСтроки);
Для Каждого Элемент Из МассивА Цикл
ТаблицаА.Добавить().Значение = Элемент;
КонецЦикла;
ТаблицаБ = ТаблицаА.СкопироватьКолонки();
Для Каждого Элемент Из МассивБ Цикл
ТаблицаБ.Добавить().Значение = Элемент;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Значение КАК Значение
|ПОМЕСТИТЬ ВТ_А
|ИЗ
| &ТаблицаА КАК Т
|;
|
|////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Т.Значение КАК Значение
|ПОМЕСТИТЬ ВТ_Б
|ИЗ
| &ТаблицаБ КАК Т
|;
|
|////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_А.Значение КАК Значение
|ИЗ
| ВТ_А КАК ВТ_А
|
|ОБЪЕДИНИТЬ
|
|ВЫБРАТЬ
| ВТ_Б.Значение
|ИЗ
| ВТ_Б КАК ВТ_Б";
Запрос.УстановитьПараметр("ТаблицаА", ТаблицаА);
Запрос.УстановитьПараметр("ТаблицаБ", ТаблицаБ);
Результат = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Значение");
Разница между двумя операторами:
| Оператор | Дубли | Скорость |
|---|---|---|
ОБЪЕДИНИТЬ | Убирает | Медленнее: СУБД ищет повторы |
ОБЪЕДИНИТЬ ВСЕ | Оставляет | Быстрее: просто склейка |
Для строк строковый квалификатор обязателен: неограниченная строка в запросе ведёт себя плохо при сравнении и группировке. 150 символов в примере — подберите длину под свои данные.
Скорость на больших массивах
Пока массивы небольшие, выбирайте самый читаемый способ. Когда счёт идёт на десятки тысяч элементов, начинает играть роль алгоритм.
| Способ | Подходит для | Ограничения |
|---|---|---|
Цикл с Добавить() | Любой объём, дубли не важны | Дубли остаются |
Найти() в цикле | До нескольких сотен элементов | Время растёт квадратично |
Соответствие | Тысячи и десятки тысяч, уникальность | Нужен ключ, сравнимый по значению |
Таблица значений + Свернуть() | Свёртка с суммами, сортировка | Только сервер и толстый клиент |
Запрос с В или ОБЪЕДИНИТЬ | Ссылки, нужны реквизиты | Серверный вызов, подготовка таблиц |
Что ещё ускоряет работу:
- ⚡ Не гоняйте массив между клиентом и сервером. Каждый серверный вызов сериализует параметры. Соберите и объедините данные в одной процедуре
&НаСервереи верните на клиент только результат. - 📦 Не вызывайте
Количество()иНайти()там, где можно обойтись без них. В циклеДля Каждогосчётчик не нужен вовсе. - 🧮 Создавайте массив нужного размера, если он известен.
Новый Массив(МассивА.Количество() + МассивБ.Количество())создаёт массив, заполненный значениямиНеопределено, который потом заполняют черезУстановить()или присваивание по индексу. Выигрыш небольшой, но для очень больших массивов заметен. - 🗄️ Ссылки — в запрос. Если после объединения вы всё равно читаете реквизиты элементов через точку, одним запросом выйдет быстрее, чем объединить в цикле и потом обращаться к каждому элементу.
Пример с заранее заданным размером:
КолА = МассивА.Количество();
КолБ = МассивБ.Количество();
Результат = Новый Массив(КолА + КолБ);
Для Индекс = 0 По КолА - 1 Цикл
Результат[Индекс] = МассивА[Индекс];
КонецЦикла;
Для Индекс = 0 По КолБ - 1 Цикл
Результат[КолА + Индекс] = МассивБ[Индекс];
КонецЦикла;
Важно не перепутать: если создать массив размером КолА + КолБ и потом вызывать Добавить(), элементы встанут после пустых значений и массив окажется вдвое длиннее.
Ошибки при слиянии массивов и их отладка
Количество() - 1 или отрицательный. Чаще всего цикл Для Индекс = 0 По Массив.Количество() без - 1. Проверьте границу цикла и длину второго массива при поэлементной склейке.МассивВ = МассивА скопировало ссылку, а не данные. Создавайте копию через Новый Массив(Новый ФиксированныйМассив(МассивА)) или собирайте результат в новый массив.Ложь. Кроме того, процедура не убирает повторы, которые уже были в первом массиве: сверните результат через СвернутьМассив().Соответствие.Новый ОписаниеТипов(...) при добавлении колонки и кладите туда только примитивы и ссылки.&НаСервере.ФиксированныйМассив (например, полученный из параметра сеанса или реквизита). Его нельзя изменять: создайте обычный массив Новый Массив(ФиксированныйМассив).Для отладки удобно остановиться точкой останова после объединения и открыть результат в окне «Вычислить выражение» (Shift+F9) — там видно количество элементов и значение каждого. Для проверки на дубли в отладчике можно вычислить ОбщегоНазначенияКлиентСервер.СвернутьМассив(Результат).Количество() и сравнить с Результат.Количество().
Можно ли объединить массивы одной строкой без цикла и без БСП?
Массив нет метода конкатенации. Без БСП нужен цикл или своя функция в общем модуле.Чем Массив отличается от ФиксированныйМассив при объединении?
Добавить и Вставить у него нет. Его можно читать в цикле как источник, но приёмником должен быть обычный Массив.Как найти элементы, которые есть только в одном из массивов?
ОбщегоНазначенияКлиентСервер.РазностьМассивов(МассивА, МассивБ) — она возвращает элементы первого массива, которых нет во втором. Без БСП — через Соответствие из второго массива и проверку каждого элемента первого.Как объединить два массива одинаковой длины в пары?
0 до Количество() - 1 и для каждого индекса создайте Новый Структура("Ключ, Значение", МассивА[Индекс], МассивБ[Индекс]) или строку таблицы значений с двумя колонками.Сохранится ли порядок элементов после ОБЪЕДИНИТЬ в запросе?
УПОРЯДОЧИТЬ ПО порядок строк в результате запроса не гарантирован. Если порядок важен, добавьте сортировку или объединяйте в коде.