Массивы в 1С: создание и наполнение данными в цикле
Содержание
Массив в 1С создаётся одной строкой — Массив = Новый Массив;, а заполняется либо методом Добавить, либо присваиванием по индексу внутри цикла Для ... По ... Цикл. Главное, что ломает большинство примеров: индексы начинаются с нуля, последний элемент имеет номер Количество() - 1.
Если вы раз за разом ловите «Индекс находится за границами массива» или не понимаете, откуда в массиве взялось Неопределено, дело почти всегда в границах цикла и в том, как массив был создан. Ниже — рабочие конструкции на встроенном языке 1С 8.3: как инициализировать массив под нужный размер, чем Добавить отличается от присваивания по индексу, как собрать двумерную структуру и чем заменить ручной перебор на больших объёмах.
Как создать массив и подготовить его к заполнению
Создание массива — это вызов конструктора Новый Массив. У него два основных варианта.
Пустой массив без элементов — когда количество заранее неизвестно и вы будете добавлять значения по одному:
Массив = Новый Массив;
Массив заданного размера — когда число элементов известно. Все ячейки сразу получают значение Неопределено:
Массив = Новый Массив(10); // 10 элементов, каждый = Неопределено
Разница принципиальная. В первом случае Массив.Количество() равно нулю, и обратиться к Массив[0] нельзя — элемента ещё нет. Во втором Количество() сразу равно 10, и Массив[0] уже существует, просто хранит Неопределено.
Если точный размер известен до цикла, объявляйте массив через Новый Массив(Размер) и заполняйте по индексу. Это избавляет платформу от многократного расширения коллекции по ходу добавления.
Отдельно стоит ФиксированныйМассив — неизменяемая обёртка над обычным массивом. Её удобно возвращать из функций, чтобы вызывающий код не смог случайно изменить данные:
Данные = Новый Массив;
Данные.Добавить("Январь");
Данные.Добавить("Февраль");
Результат = Новый ФиксированныйМассив(Данные);
В ФиксированныйМассив добавлять элементы уже нельзя — он только для чтения.
Заполнение массива в цикле: Добавить и присваивание по индексу
Есть два базовых способа положить значения в массив внутри цикла.
Способ 1. Метод Добавить для массива неизвестного размера
Метод Добавить(Значение) кладёт элемент в конец массива и увеличивает Количество() на единицу. Классический пример — собрать квадраты чисел от 1 до 10:
Массив = Новый Массив;
Для Число = 1 По 10 Цикл
Массив.Добавить(Число * Число);
КонецЦикла;
// Массив[0] = 1, Массив[1] = 4, ... Массив[9] = 100
Обратите внимание: счётчик Число идёт от 1 до 10, а индексы в массиве получаются с 0 до 9. Это нормально — порядок добавления сохраняется, индексация просто сдвинута на единицу.
Способ 2. Присваивание по индексу для массива известного размера
Если массив создан с размером, элементы заполняют прямым присваиванием. Счётчик цикла идёт строго от 0 до Количество() - 1:
Количество = 10;
Массив = Новый Массив(Количество);
Для Индекс = 0 По Количество - 1 Цикл
Массив[Индекс] = Индекс * 10;
КонецЦикла;
// Массив[0] = 0, Массив[1] = 10, ... Массив[9] = 90
Граница цикла — именно Количество() - 1, а не Количество(). Иначе на последней итерации вы обратитесь к несуществующему индексу и получите ошибку.
Полезная особенность: массив в 1С не привязан к одному типу. В одной коллекции спокойно соседствуют число, строка, ссылка на справочник и даже вложенный массив. Это удобно для разнородных данных, но накладывает ответственность — перед вычислениями с элементом убедитесь, что он нужного типа, через ТипЗнч(Элемент). Если по индексу хранится не то, что вы ждёте, арифметика или обращение к реквизиту упадут с ошибкой уже во время выполнения.
Для вставки значения в середину есть метод Вставить(Индекс, Значение) — он сдвигает остальные элементы вправо:
Массив.Вставить(0, "Первый"); // вставить в начало
Сравним оба подхода:
| Приём | Когда применять | Что с размером |
|---|---|---|
Добавить(Значение) | размер неизвестен, собираем по одному | растёт автоматически |
Присваивание Массив[i] = ... | размер известен заранее | задан при создании |
Вставить(Индекс, Значение) | нужно вставить не в конец | растёт, элементы сдвигаются |
Многомерные массивы и вложенные коллекции
Двумерную структуру (таблицу из строк и столбцов) в 1С собирают двумя способами.
Через конструктор с двумя параметрами. Новый Массив(3, 4) создаёт массив из 3 элементов, каждый из которых — вложенный массив на 4 ячейки. Обращение идёт через два индекса Массив[Строка][Столбец]:
Строки = 3;
Столбцы = 4;
Таблица = Новый Массив(Строки, Столбцы);
Для НомерСтроки = 0 По Строки - 1 Цикл
Для НомерСтолбца = 0 По Столбцы - 1 Цикл
Таблица[НомерСтроки][НомерСтолбца] = НомерСтроки * 10 + НомерСтолбца;
КонецЦикла;
КонецЦикла;
Через массив массивов вручную. Этот вариант гибче, когда строки имеют разную длину:
Таблица = Новый Массив;
Для НомерСтроки = 1 По 3 Цикл
Строка = Новый Массив;
Для НомерСтолбца = 1 По НомерСтроки Цикл // длина строки разная
Строка.Добавить(НомерСтолбца);
КонецЦикла;
Таблица.Добавить(Строка);
КонецЦикла;
При ручном создании обязательно объявляйте вложенный массив Строка = Новый Массив внутри внешнего цикла. Если вынести эту строку наружу, все «строки» будут ссылаться на один и тот же объект, и данные затрут друг друга.
Если по смыслу у вас не просто матрица чисел, а записи с именованными полями, многомерный массив часто неудобен. Под такие данные в 1С есть ТаблицаЗначений с колонками по именам или Массив из структур — к ним вернёмся в разделе про альтернативы.
Как ускорить заполнение больших массивов
На десятках тысяч элементов видна разница в подходах.
🔹 Задавайте размер заранее, если он известен. Создание Новый Массив(Количество) и присваивание по индексу избавляет платформу от постепенного расширения внутреннего буфера, которое происходит при частых вызовах Добавить.
🔹 Не пересчитывайте Количество() в условии цикла без нужды. В конструкции Для Инд = 0 По Массив.Количество() - 1 Цикл платформа вычисляет границу один раз, это нормально. А вот вызывать Массив.Количество() повторно внутри тела на каждой итерации — лишняя работа; сохраните значение в переменную.
🔹 Собирайте данные пачкой, а не поэлементно. Самый частый источник тормозов — массив заполняют в цикле по выборке из запроса, вызывая Добавить на каждую строку. Если нужен просто список значений одной колонки, запрос отдаёт его сразу:
Выборка = Результат.Выгрузить(); // ТаблицаЗначений
МассивКодов = Выборка.ВыгрузитьКолонку("Код"); // готовый массив
Это заметно быстрее ручного перебора: один вызов платформы вместо тысяч.
🔹 Минимизируйте работу внутри цикла. Любой вызов метода, обращение к реквизиту ссылки или конвертация типов внутри тела цикла умножается на число итераций. Выносите за пределы цикла всё, что не меняется от итерации к итерации.
Если же массив — это промежуточный буфер для последующего поиска по значению, подумайте, не подойдёт ли вместо него Соответствие: поиск по ключу в нём работает быстрее, чем метод Найти по массиву, который перебирает элементы подряд.
Ошибки при работе с массивами в цикле
Три из четырёх проблем с массивами сводятся к границам индексов и к изменению коллекции прямо во время её обхода.
Количество() - 1, а не Количество(). Проверьте также обращения Массив[i + 1] у последнего элемента.Новый Массив(N) — все ячейки инициализированы Неопределено, и какая-то из них осталась незаполненной. Либо присвойте всем элементам значения, либо создавайте пустой массив и используйте Добавить.Удалить(i) элементы сдвигаются влево, и следующий оказывается под уже пройденным индексом. Удаляйте в обратном цикле — от последнего элемента к первому.Для Каждого ... Из ... Цикл. Для изменения состава используйте цикл по индексу или собирайте изменения в отдельный массив и применяйте их после обхода.Разберём удаление подробнее — это самая коварная из ошибок. Допустим, нужно убрать из массива все нули. Прямой цикл их пропустит:
// НЕВЕРНО: после Удалить соседний элемент сдвигается и не проверяется
Для Инд = 0 По Массив.Количество() - 1 Цикл
Если Массив[Инд] = 0 Тогда
Массив.Удалить(Инд);
КонецЕсли;
КонецЦикла;
Правильно идти с конца. У цикла Для ... По ... Цикл в 1С нет шага — он всегда прибавляет единицу, поэтому для движения в обратную сторону берут Пока с ручным уменьшением счётчика:
Инд = Массив.Количество() - 1;
Пока Инд >= 0 Цикл
Если Массив[Инд] = 0 Тогда
Массив.Удалить(Инд);
КонецЕсли;
Инд = Инд - 1;
КонецЦикла;
Когда удаляем с конца, сдвиг влево затрагивает только уже пройденные индексы, и ни один элемент не пропускается. Это стандартный приём для безопасного удаления из коллекции во время перебора.
Для отладки удобно поставить точку останова на строке с Добавить или присваиванием и смотреть массив в окне «Табло»: там видно текущее Количество() и значения по индексам на каждой итерации.
Чем заменить ручной перебор при наполнении
Писать цикл Для руками нужно не всегда — часто есть способ короче и надёжнее.
Обход готовой коллекции через Для Каждого. Когда индекс как таковой не нужен, а важны сами значения, читать массив удобнее так:
Для Каждого Элемент Из Массив Цикл
Сообщить(Элемент);
КонецЦикла;
Этот цикл не даёт выйти за границы по определению — платформа сама проходит ровно по существующим элементам. Обратная сторона: текущего индекса внутри Для Каждого нет. Если по ходу перебора нужен номер элемента (например, чтобы вывести нумерованный список), заведите отдельный счётчик и увеличивайте его вручную либо вернитесь к циклу по индексу.
Выгрузка колонки из запроса или таблицы значений. Если массив нужен как список значений одного поля, не перебирайте строки вручную — метод ВыгрузитьКолонку вернёт готовый массив (пример был в разделе про производительность).
Разбор строки в массив. Когда исходные данные — строка с разделителями, массив собирается одним вызовом СтрРазделить:
Строка = "10,20,30,40";
Массив = СтрРазделить(Строка, ",");
// Массив[0] = "10", Массив[1] = "20", ...
Учтите: элементы здесь — строки. Если нужны числа, пройдитесь по массиву и приведите значения через Число().
Выбор подходящей коллекции вместо массива. Массив хранит значения по порядку и ищет перебором. Если задача другая — присмотритесь к родственным типам:
- 🔸
Соответствие— пары «ключ — значение», быстрый поиск по ключу:Соответствие.Вставить(Ключ, Значение). - 🔸
СписокЗначений— значения с представлением и пометкой, удобен для форм и выбора пользователем. - 🔸
ТаблицаЗначений— данные с именованными колонками, когда у каждой записи несколько полей. - 🔸
Структура— фиксированный набор именованных реквизитов одной записи.
Массив незаменим там, где важен именно порядок и доступ по числовому индексу. Для всего остального почти всегда есть более точный тип.
Чем массив отличается от списка значений?
СписокЗначений дополнительно хранит у каждого элемента представление и пометку и умеет сортироваться и выводиться на форму для выбора. Для внутренних вычислений берут массив, для взаимодействия с пользователем — список значений.Как узнать, есть ли значение в массиве?
Найти(Значение) возвращает индекс первого совпадения или Неопределено, если значения нет. Проверка выглядит так: Если Массив.Найти(Искомое) <> Неопределено Тогда. Для частого поиска по большому массиву лучше заранее переложить данные в Соответствие.Как удалить элемент массива по значению, а не по индексу?
Найти, затем удалите: Инд = Массив.Найти(Значение); Если Инд <> Неопределено Тогда Массив.Удалить(Инд) КонецЕсли;. Если таких значений несколько, повторяйте в цикле или идите обратным циклом по индексам.Как очистить массив целиком?
Очистить() удаляет все элементы, после него Количество() равно нулю. Сам объект массива при этом остаётся прежним — все ссылки на него продолжают указывать на теперь уже пустой массив.Как объединить два массива в один?
Для Каждого и добавляйте элементы в первый через Добавить. Если важно исключить повторы — перед добавлением проверяйте Найти.