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