Массив во встроенном языке 1С 8.3: от создания до быстрых алгоритмов
Содержание
Работать с массивами в 1С эффективно — значит знать десяток методов объекта Массив и три правила: индексы начинаются с нуля, удалять элементы нужно с конца, а частый поиск лучше делать через Соответствие, а не через Найти() в цикле. Всё остальное — частные случаи этих правил.
Массив — самая простая универсальная коллекция встроенного языка. Он доступен и на сервере, и на клиенте, хранит значения любых типов вперемешку и не требует описания колонок. Ниже — рабочие приёмы с кодом для платформы 8.3, которые можно сразу копировать в модуль.
Как создать массив в 1С и заполнить его начальными значениями
Массив создают конструктором Новый. Вариантов три.
Пустой массив
Товары = Новый Массив;
// Товары.Количество() = 0, Товары.ВГраница() = -1
Это самый частый случай: создали и наполняем по ходу алгоритма.
Массив заданного размера
Месяцы = Новый Массив(12);
// 12 элементов, каждый равен Неопределено
Месяцы[0] = "Январь";
Месяцы[11] = "Декабрь";
Размер, переданный в конструктор, — это не «вместимость», а реальное количество элементов. Если потом вызвать Месяцы.Добавить("Тринадцатый"), элемент встанет на позицию 12, а не на первое пустое место.
Многомерный массив
Матрица = Новый Массив(3, 4);
// 3 строки по 4 элемента
Матрица[0][2] = 15;
По сути это массив массивов. Обращение идёт через двойные скобки: сначала индекс строки, затем столбца.
Быстрые способы получить готовый массив
Часто массив не создают вручную — его возвращают другие объекты:
- 🔹
СтрРазделить("А,Б,В", ",")— массив строк из строки с разделителем (платформа 8.3.6 и новее); - 🔹
ТаблицаЗначений.ВыгрузитьКолонку("Номенклатура")— колонка таблицы в виде массива; - 🔹
РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка")— ссылки из запроса; - 🔹
СписокЗначений.ВыгрузитьЗначения()— значения списка без представлений; - 🔹
Новый Массив(ФиксированныйМассив)— изменяемая копия фиксированного массива.
Если массив нужен «только для чтения», например для передачи в параметры общего модуля с повторным использованием возвращаемых значений, оберните его: Новый ФиксированныйМассив(Товары). Изменить такой массив случайно уже не получится.
Добавление и вставка элементов в массив
Методов два, и они отличаются местом, куда попадает значение.
| Метод | Куда ставит элемент | Когда использовать |
|---|---|---|
Добавить(Значение) | В конец массива | Наполнение в цикле, сбор результатов |
Вставить(Индекс, Значение) | На позицию Индекс, остальные сдвигаются вправо | Нужен порядок: вставка в начало или середину |
Установить(Индекс, Значение) или Массив[Индекс] = Значение | Заменяет существующий элемент | Правка значения без изменения размера |
Пример наполнения:
Суммы = Новый Массив;
Суммы.Добавить(1500);
Суммы.Добавить(2700);
Суммы.Вставить(0, 900);
// Суммы: 900, 1500, 2700
Суммы[2] = 3000;
// Суммы: 900, 1500, 3000
Объединение двух массивов
Встроенного метода «слить массивы» у объекта нет. Пишут цикл:
Для Каждого Элемент Из Источник Цикл
Приемник.Добавить(Элемент);
КонецЦикла;
В типовых конфигурациях на БСП это уже сделано за вас: ОбщегоНазначенияКлиентСервер.ДополнитьМассив(Приемник, Источник, Истина). Третий параметр Истина добавит только те значения, которых в приёмнике ещё нет.
Обращение через квадратные скобки к несуществующему индексу не расширяет массив. Суммы[10] = 1 при трёх элементах даст ошибку Индекс находится за границами массива. Чтобы дописать элемент, используйте Добавить().
Удаление элементов из массива: по индексу, по значению, все сразу
По индексу
Суммы.Удалить(1);
// удалён второй элемент, хвост сдвинулся влево
После удаления индексы всех следующих элементов уменьшаются на единицу. На этом и спотыкаются при удалении в цикле.
По значению
Метод Удалить() принимает индекс, а не значение. Сначала ищем позицию:
Индекс = Суммы.Найти(1500);
Если Индекс <> Неопределено Тогда
Суммы.Удалить(Индекс);
КонецЕсли;
Так удаляется только первое вхождение.
Все элементы по условию
Правильный способ — идти с конца. Тогда удаление текущего элемента сдвигает только уже проверенный хвост:
ПоследнийИндекс = Суммы.ВГраница();
Для Сч = 0 По ПоследнийИндекс Цикл
Индекс = ПоследнийИндекс - Сч;
Если Суммы[Индекс] < 1000 Тогда
Суммы.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Границу вычисляем один раз до цикла и от неё отсчитываем индекс. Если пересчитывать Суммы.Количество() на каждой итерации, после удаления индекс «перепрыгнет» через элемент.
Не удаляйте элементы внутри цикла Для Каждого по тому же массиву — часть элементов будет пропущена, и ошибка не всплывёт сразу.
Для удаления всех вхождений конкретного значения в БСП есть ОбщегоНазначенияКлиентСервер.УдалитьВсеВхожденияЗначенияИзМассива(Массив, Значение).
Полная очистка
Суммы.Очистить() удаляет все элементы, но переменная продолжает указывать на тот же объект. Это важно, если массив передан в другую процедуру: там он тоже станет пустым. Если нужен новый пустой массив, а старый должен остаться, присвойте Суммы = Новый Массив.
Поиск в массиве и проверка, есть ли значение
Метод Найти()
Массив.Найти(Значение) возвращает индекс первого совпадения или Неопределено, если значения нет. Проверка наличия выглядит так:
Если Склады.Найти(ОсновнойСклад) <> Неопределено Тогда
// склад есть в списке
КонецЕсли;
Сравнивайте результат именно с Неопределено. Запись Если Склады.Найти(Склад) Тогда сломается: найденный элемент на позиции 0 вернёт число 0, а не булево значение.
Что Найти() считает совпадением
- ✅ Числа, строки, даты, булево — по значению.
- ✅ Ссылки на справочники и документы — по ссылке: две переменные с одной и той же ссылкой совпадут.
- ❌ Структуры, массивы, таблицы — по объекту, а не по содержимому. Две структуры с одинаковыми полями для
Найти()разные.
Если нужно искать по полю структуры, перебирайте массив и сравнивайте поле вручную. Или сразу храните данные в ТаблицаЗначений и используйте НайтиСтроки().
Поиск по условию: максимум, минимум, первый подходящий
Максимум = Неопределено;
Для Каждого Сумма Из Суммы Цикл
Если Максимум = Неопределено Или Сумма > Максимум Тогда
Максимум = Сумма;
КонецЕсли;
КонецЦикла;
Встроенных функций «максимум по массиву» у объекта нет, поэтому такие задачи решают циклом. Для чисел подойдёт и Макс(), но она принимает список параметров, а не массив.
Перебор элементов массива: какой цикл выбрать
Способов два, и у каждого своя задача.
| Цикл | Пример | Когда подходит |
|---|---|---|
Для Каждого … Из … Цикл | Для Каждого Элемент Из Товары Цикл | Читаем значения, индекс не нужен |
Для … По … Цикл с индексом | Для Индекс = 0 По Товары.ВГраница() Цикл | Нужна позиция, сосед слева или справа, замена значения |
Перебор с индексом:
Для Индекс = 0 По Товары.ВГраница() Цикл
Сообщить(СтрШаблон("%1. %2", Индекс + 1, Товары[Индекс]));
КонецЦикла;
Здесь СтрШаблон сам приводит число к строке, без лишних разделителей разрядов. Если склеиваете строки через плюс, используйте Формат(Индекс + 1, "ЧГ=0"): иначе номер 1000 выведется как «1 000».
У пустого массива ВГраница() равна -1, поэтому цикл Для Индекс = 0 По -1 просто не выполнится ни разу. Отдельная проверка на пустоту перед таким циклом не нужна.
Массив структур: перебор по полям
Когда у каждой записи несколько значений, массив наполняют структурами. Это удобно для небольших наборов данных, которые не нужно сортировать и группировать:
Строки = Новый Массив;
Строки.Добавить(Новый Структура("Товар, Количество, Цена", "Бумага А4", 10, 350));
Строки.Добавить(Новый Структура("Товар, Количество, Цена", "Скрепки", 4, 90));
Итого = 0;
Для Каждого Запись Из Строки Цикл
Итого = Итого + Запись.Количество * Запись.Цена;
КонецЦикла;
// Итого = 10 * 350 + 4 * 90 = 3860
Внутри цикла поля структуры можно читать и менять: Запись.Цена = 400 изменит элемент самого массива, потому что переменная Запись указывает на ту же структуру. Если же структур больше нескольких тысяч, а нужны сортировка, отбор и итоги, переходите на таблицу значений — там это делается одним методом.
Сортировка массива
У объекта Массив нет метода сортировки. Стандартный приём — через список значений:
Список = Новый СписокЗначений;
Список.ЗагрузитьЗначения(Суммы);
Список.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
Суммы = Список.ВыгрузитьЗначения();
Для массива структур удобнее таблица значений: загрузите данные в колонки, вызовите Сортировать("Сумма Убыв") и выгрузите нужную колонку.
Строка из массива
Чтобы вывести элементы через запятую, не нужен цикл с проверкой «последний ли это элемент»:
Текст = СтрСоединить(Коды, ", ");
Функция работает с массивом строк. Если в массиве числа или ссылки, сначала приведите элементы к строке.
Массив в запросе и при передаче между клиентом и сервером
Параметр запроса
Массив — естественный тип для условия В:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка,
| Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&СписокТоваров)";
Запрос.УстановитьПараметр("СписокТоваров", Товары);
Если массив большой — тысячи и десятки тысяч ссылок, — запрос с длинным В (&...) становится тяжёлым для СУБД. В таком случае поместите значения во временную таблицу через МенеджерВременныхТаблиц и соедините с ней. Для этого массив предварительно загружают в колонку таблицы значений методом ЗагрузитьКолонку() с типизированной колонкой.
Клиент и сервер
Массив передаётся между клиентом и сервером как параметр и как возвращаемое значение. Условие одно: все его элементы должны быть допустимы на клиенте. Числа, строки, даты, ссылки, структуры из таких значений — пройдут. Таблица значений или объект документа внутри массива — нет.
Не тяните на клиент массив из десятков тысяч ссылок, если там нужен только итог. Посчитайте на сервере и верните число или короткую структуру.
Оптимизация работы с массивами и типичные ошибки
Поиск в цикле — главный тормоз
Найти() просматривает массив подряд. Если вызывать его внутри цикла по другому массиву, время растёт квадратично: 10 000 × 10 000 — это до 100 миллионов сравнений.
Решение — один раз построить Соответствие:
ЕстьВСписке = Новый Соответствие;
Для Каждого Товар Из Разрешенные Цикл
ЕстьВСписке.Вставить(Товар, Истина);
КонецЦикла;
Для Каждого Товар Из ВсеТовары Цикл
Если ЕстьВСписке[Товар] = Истина Тогда
// обработка разрешённого товара
КонецЕсли;
КонецЦикла;
Обращение к соответствию по ключу не перебирает всю коллекцию, поэтому на больших объёмах выигрыш в разы.
Уникальные значения
Тот же приём убирает дубли:
Уникальные = Новый Массив;
Встречалось = Новый Соответствие;
Для Каждого Значение Из Исходный Цикл
Если Встречалось[Значение] = Неопределено Тогда
Встречалось.Вставить(Значение, Истина);
Уникальные.Добавить(Значение);
КонецЕсли;
КонецЦикла;
Порядок первого появления сохраняется. В конфигурациях на БСП есть готовая функция ОбщегоНазначенияКлиентСервер.СвернутьМассив(Массив).
Вставка в начало
Вставить(0, Значение) сдвигает все элементы. Для массива на сотни тысяч строк в цикле это заметно. Если нужен обратный порядок, добавляйте через Добавить() и потом перебирайте с конца.
Копия вместо ссылки
Копия = Исходный; // это НЕ копия, а та же самая коллекция
Копия.Очистить(); // Исходный тоже пуст
Массив — объект, присваивание передаёт ссылку на него. Настоящую копию первого уровня даёт Новый Массив(Новый ФиксированныйМассив(Исходный)). Для вложенных коллекций в БСП есть ОбщегоНазначения.СкопироватьРекурсивно().
Сводка частых ошибок
Частые вопросы о массивах в 1С
Чем массив отличается от списка значений?
Можно ли сделать массив реквизитом формы?
Список.ВыгрузитьЗначения().Сколько элементов может хранить массив?
Как узнать, что массив пустой?
Массив.Количество() = 0. Проверка ВГраница() = -1 даёт тот же результат, но читается хуже.Как сравнить два массива поэлементно?
Количество(), затем в цикле по индексу — значения на одинаковых позициях. Если порядок не важен, постройте Соответствие из одного массива и проверьте каждый элемент второго.