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