Массив во встроенном языке 1С 8.3: от создания до быстрых алгоритмов

· 9 мин

Содержание

Работать с массивами в 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 даёт тот же результат, но читается хуже.
Как сравнить два массива поэлементно?
Сначала сравните Количество(), затем в цикле по индексу — значения на одинаковых позициях. Если порядок не важен, постройте Соответствие из одного массива и проверьте каждый элемент второго.