Массивы в 1С: создание и наполнение данными в цикле

· 9 мин

Содержание

Массив в 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 Цикл платформа вычисляет границу один раз, это нормально. А вот вызывать Массив.Количество() повторно внутри тела на каждой итерации — лишняя работа; сохраните значение в переменную.

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

Выборка = Результат.Выгрузить(); // ТаблицаЗначений
МассивКодов = Выборка.ВыгрузитьКолонку("Код"); // готовый массив

Это заметно быстрее ручного перебора: один вызов платформы вместо тысяч.

🔹 Минимизируйте работу внутри цикла. Любой вызов метода, обращение к реквизиту ссылки или конвертация типов внутри тела цикла умножается на число итераций. Выносите за пределы цикла всё, что не меняется от итерации к итерации.

Если же массив — это промежуточный буфер для последующего поиска по значению, подумайте, не подойдёт ли вместо него Соответствие: поиск по ключу в нём работает быстрее, чем метод Найти по массиву, который перебирает элементы подряд.

Ошибки при работе с массивами в цикле

Три из четырёх проблем с массивами сводятся к границам индексов и к изменению коллекции прямо во время её обхода.

Диагностика: выберите симптом

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

// НЕВЕРНО: после Удалить соседний элемент сдвигается и не проверяется
Для Инд = 0 По Массив.Количество() - 1 Цикл
    Если Массив[Инд] = 0 Тогда
        Массив.Удалить(Инд);
    КонецЕсли;
КонецЦикла;

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

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

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

Для отладки удобно поставить точку останова на строке с Добавить или присваиванием и смотреть массив в окне «Табло»: там видно текущее Количество() и значения по индексам на каждой итерации.

Чем заменить ручной перебор при наполнении

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

Обход готовой коллекции через Для Каждого. Когда индекс как таковой не нужен, а важны сами значения, читать массив удобнее так:

Для Каждого Элемент Из Массив Цикл
    Сообщить(Элемент);
КонецЦикла;

Этот цикл не даёт выйти за границы по определению — платформа сама проходит ровно по существующим элементам. Обратная сторона: текущего индекса внутри Для Каждого нет. Если по ходу перебора нужен номер элемента (например, чтобы вывести нумерованный список), заведите отдельный счётчик и увеличивайте его вручную либо вернитесь к циклу по индексу.

Выгрузка колонки из запроса или таблицы значений. Если массив нужен как список значений одного поля, не перебирайте строки вручную — метод ВыгрузитьКолонку вернёт готовый массив (пример был в разделе про производительность).

Разбор строки в массив. Когда исходные данные — строка с разделителями, массив собирается одним вызовом СтрРазделить:

Строка = "10,20,30,40";
Массив = СтрРазделить(Строка, ",");
// Массив[0] = "10", Массив[1] = "20", ...

Учтите: элементы здесь — строки. Если нужны числа, пройдитесь по массиву и приведите значения через Число().

Выбор подходящей коллекции вместо массива. Массив хранит значения по порядку и ищет перебором. Если задача другая — присмотритесь к родственным типам:

  • 🔸 Соответствие — пары «ключ — значение», быстрый поиск по ключу: Соответствие.Вставить(Ключ, Значение).
  • 🔸 СписокЗначений — значения с представлением и пометкой, удобен для форм и выбора пользователем.
  • 🔸 ТаблицаЗначений — данные с именованными колонками, когда у каждой записи несколько полей.
  • 🔸 Структура — фиксированный набор именованных реквизитов одной записи.

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

Чем массив отличается от списка значений?
Массив — простая упорядоченная коллекция, доступ только по числовому индексу. СписокЗначений дополнительно хранит у каждого элемента представление и пометку и умеет сортироваться и выводиться на форму для выбора. Для внутренних вычислений берут массив, для взаимодействия с пользователем — список значений.
Как узнать, есть ли значение в массиве?
Метод Найти(Значение) возвращает индекс первого совпадения или Неопределено, если значения нет. Проверка выглядит так: Если Массив.Найти(Искомое) <> Неопределено Тогда. Для частого поиска по большому массиву лучше заранее переложить данные в Соответствие.
Как удалить элемент массива по значению, а не по индексу?
Сначала найдите индекс через Найти, затем удалите: Инд = Массив.Найти(Значение); Если Инд <> Неопределено Тогда Массив.Удалить(Инд) КонецЕсли;. Если таких значений несколько, повторяйте в цикле или идите обратным циклом по индексам.
Как очистить массив целиком?
Метод Очистить() удаляет все элементы, после него Количество() равно нулю. Сам объект массива при этом остаётся прежним — все ссылки на него продолжают указывать на теперь уже пустой массив.
Как объединить два массива в один?
Отдельного метода слияния у массива нет. Пройдите второй массив циклом Для Каждого и добавляйте элементы в первый через Добавить. Если важно исключить повторы — перед добавлением проверяйте Найти.