Работа с массивами в 1С 8.3: создание, методы и примеры кода

· 9 мин

Содержание

Массив в 1С создаётся одной строкой: Массив = Новый Массив;. Это универсальная коллекция встроенного языка — упорядоченный список значений, к которым обращаются по числовому индексу, начиная с нуля. В массив можно складывать значения любого типа вперемешку: строки, числа, ссылки на справочники, даже другие массивы.

Писать этот код можно в любом модуле конфигурации — в Конфигураторе или в 1С:EDT: в модуле формы, общем модуле, модуле объекта. Ниже — как объявить массив, заполнить его, перебрать, найти и удалить элемент, собрать многомерную структуру и не споткнуться на типичных ошибках индексации.

Объявление массива: пустой и заданного размера

Конструктор Новый Массив работает в трёх режимах.

Пустой массив — без аргументов. Добавляете элементы по мере надобности:

Товары = Новый Массив;

Массив фиксированной длины — с числом в скобках. Создаётся N ячеек, и все они заполнены значением Неопределено:

Остатки = Новый Массив(10); // 10 ячеек, в каждой Неопределено

Этот вариант удобен, когда размер известен заранее и вы будете заполнять ячейки по индексу через Установить, а не добавлять в конец.

Массив с указанием нескольких размерностей — несколько чисел через запятую. Это уже многомерный массив, о нём отдельный раздел ниже:

Матрица = Новый Массив(3, 3); // 3×3

Размер «фиксированного» массива не жёсткий: даже после Новый Массив(10) вы можете добавить одиннадцатый элемент через Добавить, и он встанет в индекс 10. Число в конструкторе — это стартовое заполнение, а не потолок.

Тип элементов не ограничивается. Один и тот же массив спокойно хранит разнородные данные:

Данные = Новый Массив;
Данные.Добавить("Текст");
Данные.Добавить(42);
Данные.Добавить(Дата(2024, 1, 1));
Данные.Добавить(Справочники.Номенклатура.ПустаяСсылка());

Добавление и заполнение элементов массива

Для наполнения массива есть два метода: Добавить и Вставить.

Добавить(Значение) кладёт элемент в самый конец — индекс нового элемента будет равен прежнему количеству:

Города = Новый Массив;
Города.Добавить("Москва");   // индекс 0
Города.Добавить("Казань");   // индекс 1
Города.Добавить("Самара");   // индекс 2

Вставить(Индекс, Значение) вставляет элемент на нужную позицию, сдвигая всё, что правее, на один индекс вправо:

Города.Вставить(0, "Санкт-Петербург"); // встал в начало
// теперь: Санкт-Петербург, Москва, Казань, Самара

Записать значение в уже существующую ячейку можно двумя равнозначными способами — через квадратные скобки или метод Установить:

Города[1] = "Нижний Новгород";
Города.Установить(2, "Уфа");    // то же самое для индекса 2

Разница между Установить и Вставить принципиальна: Установить перезаписывает ячейку, Вставить добавляет новую со сдвигом. Их легко перепутать, а результат будет разным.

Часто массив заполняют не руками, а из других источников. Полезные приёмы:

  • 🔹 СтрРазделить("1,2,3", ",") — вернёт массив из трёх строк.
  • 🔹 СписокЗначений.ВыгрузитьЗначения() — массив из списка значений.
  • 🔹 ТаблицаЗначений.ВыгрузитьКолонку("Номенклатура") — массив значений одной колонки.
Строки = СтрРазделить("яблоко;груша;слива", ";");
// Строки[0] = "яблоко", Строки[1] = "груша", Строки[2] = "слива"

Обращение к элементам и перебор массива

Количество элементов возвращает метод Количество():

Всего = Города.Количество(); // например, 4

Отдельный элемент читается по индексу — через скобки или метод Получить:

Первый = Города[0];
Первый = Города.Получить(0); // идентичный результат

Перебрать массив можно двумя циклами. Когда индекс сам по себе не нужен, короче и понятнее Для Каждого:

Для Каждого Город Из Города Цикл
    Сообщить(Город);
КонецЦикла;

Когда внутри цикла нужен номер позиции, перебирают по индексу. Здесь выручает метод ВГраница() — он возвращает индекс последнего элемента (то есть Количество() - 1):

Для Индекс = 0 По Города.ВГраница() Цикл
    Сообщить(Строка(Индекс) + ": " + Города[Индекс]);
КонецЦикла;

Для пустого массива ВГраница() возвращает -1, поэтому цикл Для 0 По -1 просто не выполнится ни разу — проверять массив на пустоту перед таким циклом не нужно.

Ниже — короткий тест: закрепите, чем отличаются способы обращения к элементам.

Проверьте себя
Какой индекс у первого элемента массива в 1С?
Что вернёт `ВГраница()` для массива из 5 элементов?
Как перебрать массив, если внутри цикла нужен номер позиции?

Поиск, удаление и очистка значений

Найти элемент по значению помогает метод Найти(Значение). Он возвращает индекс первого совпадения или Неопределено, если значения в массиве нет:

Индекс = Города.Найти("Казань");
Если Индекс <> Неопределено Тогда
    Сообщить("Казань стоит под индексом " + Индекс);
Иначе
    Сообщить("Казани в списке нет");
КонецЕсли;

Найти возвращает именно Неопределено, а не -1. Сравнение результата с -1 — распространённая ошибка, перенесённая из других языков: такое условие всегда ложно, и код ведёт себя непредсказуемо.

Удаление элемента — метод Удалить(Индекс). Он принимает индекс, а не само значение, поэтому часто идёт в паре с Найти:

Поз = Города.Найти("Самара");
Если Поз <> Неопределено Тогда
    Города.Удалить(Поз);
КонецЕсли;

После удаления все элементы правее сдвигаются влево, индексы пересчитываются. Из-за этого удалять элементы внутри цикла Для Каждого нельзя — коллекция меняется прямо во время перебора, и платформа выдаст ошибку. Правильный приём — идти по индексам с конца через цикл Пока:

Индекс = Города.ВГраница();
Пока Индекс >= 0 Цикл
    Если Города[Индекс] = "Москва" Тогда
        Города.Удалить(Индекс);
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

Почему с конца: при движении от начала удаление сдвигает ещё не пройденные элементы, и один из них проскакивается. Движение справа налево этой проблемы лишено — сдвигаются только уже обработанные позиции.

Полностью опустошить массив — метод Очистить(). Переменная остаётся массивом, просто без элементов:

Города.Очистить();
Сообщить(Города.Количество()); // 0

Многомерные массивы и вложенные структуры

Многомерный массив в 1С — это массив, элементами которого являются другие массивы. Создать его можно сразу конструктором с несколькими размерностями:

Матрица = Новый Массив(2, 3); // 2 строки по 3 столбца
Матрица[0][0] = 1;
Матрица[0][1] = 2;
Матрица[1][2] = 6;

Обращение идёт по цепочке индексов: Матрица[строка][столбец]. Перебор двумерного массива — вложенные циклы:

Для Строка = 0 По Матрица.ВГраница() Цикл
    СтрокаМатрицы = Матрица[Строка];
    Для Столбец = 0 По СтрокаМатрицы.ВГраница() Цикл
        Сообщить(СтрокаМатрицы[Столбец]);
    КонецЦикла;
КонецЦикла;

Тот же результат собирается и вручную, без конструктора с размерностями, — это гибче, когда «строки» разной длины:

Таблица = Новый Массив;
Строка1 = Новый Массив;
Строка1.Добавить("Иванов");
Строка1.Добавить(50000);
Таблица.Добавить(Строка1);

На практике, когда у «столбцов» есть смысл и имя, вместо вложенных массивов удобнее массив структур — читается яснее, чем доступ по числовым индексам:

Сотрудники = Новый Массив;
Запись = Новый Структура("Фамилия, Оклад", "Иванов", 50000);
Сотрудники.Добавить(Запись);
Сотрудники.Добавить(Новый Структура("Фамилия, Оклад", "Петров", 60000));

Для Каждого Сотрудник Из Сотрудники Цикл
    Сообщить(Сотрудник.Фамилия + ": " + Сотрудник.Оклад);
КонецЦикла;

Фиксированный массив и преобразование коллекций

Иногда массив нужно защитить от изменений — например, передать его в процедуру и гарантировать, что та его не испортит. Для этого есть тип ФиксированныйМассив: он создаётся из обычного массива и доступен только на чтение.

Исходный = Новый Массив;
Исходный.Добавить("А");
Исходный.Добавить("Б");

Защищённый = Новый ФиксированныйМассив(Исходный);
// Защищённый[0] читается, но Защищённый.Добавить(...) вызовет ошибку

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

С фиксированными массивами сталкиваешься и не создавая их вручную. Значения, которые платформа кэширует между вызовами (функции с ПовторноеИспользованиеВозвратов), возвращают именно фиксированные коллекции — чтобы вызывающий код случайно не изменил общий кэш. Если попытаться добавить элемент в такой результат, получите ошибку о неизменяемости. Решение то же: сделайте изменяемую копию через Новый Массив(...) и работайте с ней.

Массив легко превращается в другие коллекции и обратно:

Что нужноКак сделать
Массив → список значенийСписок.ЗагрузитьЗначения(Массив)
Список значений → массивСписок.ВыгрузитьЗначения()
Строка → массивСтрРазделить(Строка, Разделитель)
Массив → строкаСтрСоединить(Массив, Разделитель)
Колонка таблицы → массивТаблица.ВыгрузитьКолонку("Имя")
Числа = СтрРазделить("10 20 30", " ");       // массив из 3 строк
ОднойСтрокой = СтрСоединить(Числа, " + ");    // "10 + 20 + 30"

Чтобы скопировать массив (а не передать ссылку на тот же объект), встроенного метода нет — передайте его в конструктор Новый Массив(Старый) либо перенесите элементы циклом. Для глубокой копии с вложенными коллекциями пригодится функция ОбщегоНазначения.СкопироватьРекурсивно из библиотеки стандартных подсистем.

Типичные ошибки и производительность

Самая частая ошибка новичка — выход за границы массива. Платформа сообщает об этом так: Индекс находится за границами массива.

Причины почти всегда одни и те же:

  • 🔸 обращение к Массив[Массив.Количество()] вместо Массив[Массив.Количество() - 1] — забыли, что индексы с нуля;
  • 🔸 чтение из пустого массива — перед обращением к [0] проверяйте Количество() > 0;
  • 🔸 сравнение результата Найти с -1 вместо Неопределено;
  • 🔸 удаление элементов внутри Для Каждого — меняйте массив только по индексам через Пока.

Теперь про скорость. Массив устроен как непрерывный список, и это задаёт его сильные и слабые стороны:

  • ✅ Добавить в конец и чтение по индексу — быстрые операции.
  • ❌ Вставить и Удалить в середине — медленные: платформа сдвигает все элементы правее. На больших массивах в цикле это ощутимо.
  • ❌ Найти перебирает массив с начала. Один вызов недорог, но поиск в цикле по большому массиву даёт квадратичную сложность и подвешивает обработку.

Если по данным нужен частый поиск по ключу — массив не ваш инструмент. Для этого есть Соответствие (Map): поиск по ключу в нём идёт за константное время независимо от размера.

// Медленно: поиск в цикле по массиву
Для Каждого Элемент Из БольшойМассив Цикл
    Если КлючиДляУдаления.Найти(Элемент) <> Неопределено Тогда
        // ... каждый Найти — проход по всему массиву
    КонецЕсли;
КонецЦикла;

// Быстро: ключи заранее в Соответствии
Ключи = Новый Соответствие;
Для Каждого Ключ Из КлючиДляУдаления Цикл
    Ключи.Вставить(Ключ, Истина);
КонецЦикла;
// теперь проверка Ключи.Получить(Элемент) <> Неопределено мгновенная

Ещё один скрытый расход — массивы как параметры запроса. Когда вы передаёте массив ссылок в параметр запроса и используете его в условии В (&Массив), платформа разворачивает его во временную таблицу. На небольших списках это норма, но массив на десятки тысяч ссылок лучше заранее поместить во временную таблицу запроса явно — так оптимизатор построит план адекватнее.

Практический ориентир: до нескольких тысяч элементов массив ведёт себя хорошо в любых операциях. На десятках и сотнях тысяч продумывайте структуру заранее — либо Соответствие для поиска, либо ТаблицаЗначений, если данные табличные и их нужно сортировать и группировать.

Частые вопросы

С какого индекса начинается нумерация элементов массива в 1С?
С нуля. Первый элемент — Массив[0], последний — Массив[Массив.Количество() - 1] или Массив[Массив.ВГраница()]. Это главный источник ошибки «Индекс находится за границами массива».
Чем массив отличается от списка значений и таблицы значений?
Массив — самая простая коллекция: только значения и их порядок. У списка значений дополнительно есть представление, пометка и признак выбора по каждому элементу. Таблица значений — это набор строк с именованными колонками, её можно сортировать и группировать. Для простого перечня берут массив, для табличных данных — таблицу.
Можно ли хранить в одном массиве значения разных типов?
Да. Один массив спокойно содержит строки, числа, даты, ссылки и даже вложенные массивы и структуры одновременно. Ограничений по типам у элементов нет.
Как узнать, пуст ли массив?
Проверить Массив.Количество() = 0. Отдельного метода Пустой() у массива нет. Для безопасного перебора по индексам отдельная проверка обычно и не нужна: у пустого массива ВГраница() равен -1, и цикл Для 0 По ВГраница() не выполнится.
Как отсортировать массив?
Прямого метода сортировки у массива нет. Элементы выгружают в список значений (Список.ЗагрузитьЗначения(Массив)), вызывают Список.СортироватьПоЗначению() и возвращают обратно через ВыгрузитьЗначения(). Либо используют таблицу значений с методом Сортировать.