Работа с массивами в 1С 8.3: создание, методы и примеры кода
Содержание
Массив в 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. Сравнение результата с -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 По ВГраница() не выполнится.Как отсортировать массив?
Список.ЗагрузитьЗначения(Массив)), вызывают Список.СортироватьПоЗначению() и возвращают обратно через ВыгрузитьЗначения(). Либо используют таблицу значений с методом Сортировать.