Массив в 1С: создание, добавление элементов и работа с данными

· 9 мин

Содержание

Массив в 1С создаётся конструктором Новый Массив, а элементы добавляют методом Добавить(). Это коллекция значений с доступом по индексу, нумерация начинается с нуля. Сам массив — встроенный тип платформы 8.3, работает одинаково в Бухгалтерии, ЗУП и любой другой конфигурации, потому что относится к языку, а не к прикладному решению.

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

Как объявить массив и задать начальные значения

Самый частый способ — создать пустой массив и наполнять его по ходу:

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

Количество элементов у такого массива — ноль. Если заранее известно, сколько ячеек нужно, размер задаётся в скобках. Все ячейки при этом заполняются значением Неопределено:

Остатки = Новый Массив(5);   // 5 элементов, все = Неопределено
Сообщить(Остатки.Количество()); // 5

Массив может быть многомерным. Новый Массив(3, 2) создаёт массив из трёх элементов, каждый из которых — массив из двух ячеек. Обращение идёт по двойному индексу:

Таблица = Новый Массив(3, 2);
Таблица[0][1] = "ячейка";

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

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

Как добавить элемент: Добавить, Вставить и Установить

Три метода решают три разные задачи, и их легко перепутать.

Добавить(Значение) — кладёт элемент в конец массива. Это то, что имеют в виду под «добавить массив»: наполнение коллекции по одному элементу.

Номенклатура = Новый Массив;
Номенклатура.Добавить("Молоток");
Номенклатура.Добавить("Отвёртка");
Номенклатура.Добавить("Шуруповёрт");
// теперь 3 элемента: [0]=Молоток, [1]=Отвёртка, [2]=Шуруповёрт

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

Номенклатура.Вставить(1, "Гвозди");
// [0]=Молоток, [1]=Гвозди, [2]=Отвёртка, [3]=Шуруповёрт

Установить(Индекс, Значение) — заменяет значение в существующей ячейке, не меняя размер массива. Индекс обязан быть в пределах массива, иначе платформа выдаст ошибку о выходе за границы.

Номенклатура.Установить(0, "Молоток слесарный");
// заменили первый элемент, количество прежнее

Тот же результат даёт присваивание через квадратные скобки — это синоним Установить:

Номенклатура[0] = "Молоток слесарный";

Разница в одном: Добавить увеличивает размер, Установить и [индекс] = только меняют содержимое уже существующей ячейки.

Практический пример: собрать массив из данных базы

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

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ Услуга";
Выборка = Запрос.Выполнить().Выбрать();

МассивУслуг = Новый Массив;
Пока Выборка.Следующий() Цикл
    МассивУслуг.Добавить(Выборка.Ссылка);
КонецЦикла;

Готовый массив удобно отдать в запрос как параметр для условия В (&МассивУслуг) — это стандартный приём фильтрации по списку значений. Так же наполняют массив из табличной части документа, перебирая её строки и добавляя нужный реквизит каждой строки.

Чтение элемента по индексу

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

Первый = Номенклатура[0];
Первый = Номенклатура.Получить(0); // то же самое

Количество элементов даёт Количество(), а индекс последнего элемента — ВГраница() (это всегда Количество() - 1; у пустого массива ВГраница() вернёт -1).

Почему массив — ссылочный тип и как его копировать

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

А = Новый Массив;
А.Добавить(10);

Б = А;              // Б и А указывают на ОДИН массив
Б.Добавить(20);

Сообщить(А.Количество()); // 2, хотя добавляли в Б!

Присваивание Б = А не копирует массив — обе переменные ссылаются на один объект, и изменение через любую из них видно через обе. Если нужна независимая копия, элементы переносят в новый массив вручную:

Копия = Новый Массив;
Для Каждого Элемент Из А Цикл
    Копия.Добавить(Элемент);
КонецЦикла;

Такой перебор даёт поверхностную копию: структура массива отдельная, но если внутри лежат другие ссылочные объекты (вложенные массивы, структуры), они по-прежнему общие. Для полностью независимой копии со вложенностью значение сериализуют — например, через ЗначениеВСтрокуВнутр() и обратно ЗначениеИзСтрокиВнутр():

Копия = ЗначениеИзСтрокиВнутр(ЗначениеВСтрокуВнутр(А));

Сериализация работает только для значений, которые её поддерживают (примитивы, ссылки, стандартные коллекции). Произвольные объекты так не копируются.

Та же природа ссылочного типа проявляется при передаче массива в процедуру или функцию. Внутри вызываемого метода работает тот же объект, что и у вызывающего кода. Добавили элемент внутри процедуры — он появится и в исходном массиве после возврата. Это удобно, когда процедура должна наполнить переданный массив, но легко приводит к незаметным побочным эффектам, если об этом забыть.

Ключевое слово Знач перед параметром здесь помогает лишь отчасти: оно запрещает присвоить параметру новый объект внутри процедуры, но не защищает содержимое — вызовы Добавить и Удалить по-прежнему меняют исходный массив. Чтобы полностью изолировать данные, скопируйте массив перебором в начале процедуры и работайте с копией.

Как перебрать массив в цикле

Перебор — основная операция. Способов два, и выбор зависит от того, нужен ли индекс.

Цикл Для Каждого — когда индекс не важен. Самый читаемый вариант, перебирает элементы по порядку:

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

Цикл Для ... По — когда нужен номер элемента. Идём от нуля до верхней границы:

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

Граница именно ВГраница(), а не Количество(). Если написать По Номенклатура.Количество(), последняя итерация обратится к несуществующему индексу и вызовет ошибку выхода за границы.

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

Поиск, удаление и очистка элементов

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

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

Проверять нужно именно на <> Неопределено, а не на > 0: первый элемент стоит на индексе 0, и сравнение с нулём пропустит его.

Удаление одного элемента — метод Удалить(Индекс). Элементы правее сдвигаются влево, размер массива уменьшается на единицу:

Номенклатура.Удалить(1); // убрали элемент с индексом 1

Удаление по значению делается в два шага — сначала найти, потом удалить:

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

Полная очистка — метод Очистить(). Он убирает все элементы, массив остаётся живым объектом с нулевым количеством:

Номенклатура.Очистить();
Сообщить(Номенклатура.Количество()); // 0

Очистка не то же самое, что Номенклатура = Неопределено: после Очистить() в переменной по-прежнему массив, просто пустой, и в него можно снова добавлять элементы.

Шпаргалка по методам массива

Все методы, которые нужны в повседневной работе, в одной таблице — удобно держать перед глазами:

МетодЧто делаетМеняет размер
Добавить(Значение)кладёт элемент в конец+1
Вставить(Индекс, Значение)вставляет на позицию, сдвигая остальные+1
Установить(Индекс, Значение)заменяет значение в ячейкенет
Получить(Индекс)возвращает значение по индексунет
Найти(Значение)индекс первого совпадения или Неопределенонет
Удалить(Индекс)убирает элемент, сдвигая правые влево−1
Количество()число элементовнет
ВГраница()индекс последнего элемента (или -1)нет
Очистить()убирает все элементыдо 0

Как сравнить два массива

Прямое сравнение Массив1 = Массив2 проверяет не содержимое, а ссылку: результат будет Истина, только если обе переменные указывают на один и тот же объект в памяти. Два разных массива с одинаковыми элементами такое сравнение посчитает неравными.

Чтобы сравнить по содержимому, пишут функцию: сначала сверяют количество, потом поэлементно.

Функция МассивыРавны(М1, М2)
    Если М1.Количество() <> М2.Количество() Тогда
        Возврат Ложь;
    КонецЕсли;
    Для Индекс = 0 По М1.ВГраница() Цикл
        Если М1[Индекс] <> М2[Индекс] Тогда
            Возврат Ложь;
        КонецЕсли;
    КонецЦикла;
    Возврат Истина;
КонецФункции

Эта версия учитывает порядок: массивы [1, 2] и [2, 1] она посчитает разными. Если порядок не важен, а нужно проверить совпадение состава, элементы одного массива ищут в другом через Найти(), параллельно сверяя количество.

Быстрый приём для проверки «есть ли дубликаты» или «одинаков ли набор» — перегнать массивы в соответствие, где ключ это значение, и сравнить ключи. Но для обычной задачи «совпадают ли два списка» хватает функции выше.

Частые ошибки при работе с массивами

Собрал то, на чём теряют время чаще всего — это не повтор шагов, а именно типовые грабли.

  • 🔢 Обращение за границу. Массив[Массив.Количество()] всегда даёт ошибку: последний индекс — Количество() - 1. В циклах используйте ВГраница().
  • 🔗 Ждут копию, получают ссылку. После Б = А оба имени — один массив. Копируйте перебором или сериализацией, если нужна независимость.
  • ➕ Путают Добавить и Установить. Установить не создаёт ячейку, она меняет существующую; для нового элемента нужен Добавить.
  • 🔁 Меняют массив внутри Для Каждого. Добавление или удаление во время перебора прерывает цикл. Откладывайте изменения на потом.
  • ❓ Проверяют результат Найти как число. Найти возвращает Неопределено при отсутствии и 0 для первого элемента — сравнивайте с Неопределено, не с нулём.
  • 🧹 Путают Очистить() и присваивание Неопределено. Первое оставляет пустой массив, второе уничтожает ссылку на объект.

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

Проверьте себя

Проверьте себя
Что вернёт `Массив.ВГраница()` для массива из 4 элементов?
Как сделать независимую копию массива?
Что вернёт `Найти()`, если значение не найдено?

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

Чем массив отличается от списка значений и таблицы значений?
Массив — простой упорядоченный набор значений без имён и типов. У списка значений каждый элемент дополнительно несёт представление, пометку и картинку. Таблица значений — это строки с именованными типизированными колонками. Для простого перечня берут массив, для выбора пользователем — список значений, для табличных данных — таблицу значений.
Можно ли хранить в одном массиве значения разных типов?
Да. В одну коллекцию складывают числа, строки, ссылки, булевы и даже вложенные массивы — тип элемента не фиксируется. Платформа не требует объявлять тип заранее.
Как получить последний элемент массива?
Через верхнюю границу: Массив[Массив.ВГраница()]. Для пустого массива ВГраница() вернёт -1, поэтому перед обращением проверяйте Количество() > 0.
Есть ли ограничение на размер массива?
Жёсткого лимита в языке нет, но массив целиком держится в оперативной памяти. Миллионы элементов на клиенте приведут к расходу памяти и замедлению; большие объёмы лучше обрабатывать запросом или выборкой, а не загружать в массив.
Как добавить в массив сразу несколько значений из другого массива?
Отдельного метода слияния нет — перебирают источник циклом Для Каждого и каждый элемент кладут через Добавить(). Тот же приём используют, чтобы объединить два массива в один.