Чтение элементов массива во встроенном языке 1С:Предприятия 8.3 на примерах
Содержание
Чтобы получить значение из массива в 1С, обратитесь к элементу по индексу в квадратных скобках: Значение = Массив[0];. Нумерация начинается с нуля, последний элемент — Массив[Массив.ВГраница()]. Если нужно пройти все элементы, используйте Для Каждого … Из … Цикл, а если ищете конкретное значение — метод Найти(), он вернёт индекс или Неопределено.
Типичная ситуация: запрос выгрузил колонку ссылок в массив, а дальше нужно взять первую ссылку, проверить, есть ли среди них нужный контрагент, или обойти все и что-то сделать с каждой. Ниже — все эти случаи с рабочим кодом для платформы 8.3. Синтаксис общий для любой конфигурации: БП, ЗУП, УТ или своей разработки.
Как устроен массив и откуда он берётся
Массив в 1С — упорядоченная коллекция значений любого типа. В одном массиве могут лежать числа, строки, ссылки, структуры и даже другие массивы. Каждый элемент имеет индекс — целое число от 0 до Количество() - 1.
Откуда массив чаще всего появляется в коде:
- 🧩
Новый Массив— пустой, заполняется черезДобавить(); - 📊
РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка")— колонка таблицы значений; - ✂️
СтрРазделить("a;b;c", ";")— части строки (платформа 8.3.6 и новее); - 📦 параметры процедур и возвращаемые значения общих модулей.
Методы, которые понадобятся для чтения:
| Метод | Что возвращает | Пример |
|---|---|---|
Количество() | число элементов | 3 для массива из трёх |
ВГраница() | индекс последнего элемента | 2 для массива из трёх, -1 для пустого |
Получить(Индекс) | значение по индексу | то же, что Массив[Индекс] |
Найти(Значение) | индекс первого совпадения или Неопределено | Массив.Найти("Москва") |
Запомните разницу между Количество() и ВГраница(). На ней спотыкаются чаще всего: цикл По Массив.Количество() выходит на один элемент за край.
Получение элемента массива по индексу
Самый прямой способ — квадратные скобки. Создадим массив и прочитаем элементы:
Города = Новый Массив;
Города.Добавить("Москва");
Города.Добавить("Казань");
Города.Добавить("Томск");
Первый = Города[0]; // "Москва"
Второй = Города.Получить(1); // "Казань"
Последний = Города[Города.ВГраница()]; // "Томск"
Получить() и квадратные скобки работают одинаково. Скобки короче и привычнее, Получить() иногда удобнее читать в длинных выражениях.
Перед обращением проверьте, что элемент есть
Если массив пустой или индекс больше ВГраница(), платформа выбросит исключение Индекс находится за границами диапазона. Код дальше не пойдёт, а пользователь увидит окно ошибки. Поэтому перед чтением первого элемента проверяйте количество:
Если Города.Количество() > 0 Тогда
Первый = Города[0];
Иначе
Первый = Неопределено;
КонецЕсли;
Отрицательных индексов, как в Python, в 1С нет: Города[-1] не вернёт последний элемент, а вызовет ту же ошибку.
Безопасное чтение одной функцией
В проектах удобно держать в общем модуле функцию, которая возвращает значение по умолчанию вместо ошибки:
Функция ЗначениеПоИндексу(Коллекция, Индекс, ПоУмолчанию = Неопределено) Экспорт
Если ТипЗнч(Коллекция) <> Тип("Массив")
И ТипЗнч(Коллекция) <> Тип("ФиксированныйМассив") Тогда
Возврат ПоУмолчанию;
КонецЕсли;
Если Индекс < 0 Или Индекс > Коллекция.ВГраница() Тогда
Возврат ПоУмолчанию;
КонецЕсли;
Возврат Коллекция[Индекс];
КонецФункции
Вызов: Склад = ЗначениеПоИндексу(Склады, 0, Справочники.Склады.ПустаяСсылка());. Функция сначала проверяет тип, затем границы, и только потом читает элемент.
Значение из массива, который вернул запрос
Частый сценарий — выгрузить колонку результата запроса и взять из неё значение. Здесь массив может оказаться пустым, если отбор ничего не нашёл:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 10
| Контрагенты.Ссылка КАК Ссылка
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| НЕ Контрагенты.ПометкаУдаления";
Ссылки = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
Если Ссылки.Количество() > 0 Тогда
ПервыйКонтрагент = Ссылки[0];
КонецЕсли;
Если из запроса нужна ровно одна запись, массив не нужен вовсе. Достаточно выборки: Выборка = Запрос.Выполнить().Выбрать(); Если Выборка.Следующий() Тогда … КонецЕсли;. Это экономит память и не требует проверки границ.
Перебор значений массива в цикле
Когда нужно получить все значения из массива по очереди, есть два вида цикла. Выбор зависит от того, нужен ли вам индекс.
Для Каждого — если индекс не нужен
Для Каждого Город Из Города Цикл
Сообщить(Город);
КонецЦикла;
Переменная Город на каждом шаге получает очередной элемент. Код короткий, ошибиться в границах невозможно.
Учтите одну тонкость. Присваивание Город = "Самара" внутри цикла меняет только переменную, а элемент массива остаётся прежним. Чтобы заменить значение в самом массиве, нужен индекс: Города[Индекс] = "Самара" или Города.Установить(Индекс, "Самара"). А вот если в массиве лежат структуры, изменение поля Элемент.Цена = 500 попадёт в массив: переменная указывает на тот же объект.
Для … По — если нужен номер элемента
Для Индекс = 0 По Города.ВГраница() Цикл
Сообщить(СтрШаблон("%1: %2", Формат(Индекс, "ЧН=0; ЧГ=0"), Города[Индекс]));
КонецЦикла;
Здесь граница — ВГраница(), а не Количество(). Для пустого массива ВГраница() вернёт -1, и тело цикла не выполнится ни разу — это правильное поведение.
Формат(Индекс, "ЧН=0; ЧГ=0") выводит ноль как «0» и не вставляет разделитель групп разрядов. Без него в сообщении индекс 1000 превратится в «1 000».
Обход с конца и удаление элементов
Цикл Для в 1С идёт только вперёд с шагом 1. Запись Для Индекс = Города.ВГраница() По 0 Цикл синтаксически допустима, но тело не выполнится ни разу, если в массиве больше одного элемента.
Обход с конца нужен, когда вы удаляете элементы: удаление сдвигает все последующие индексы. Надёжный вариант — цикл Пока:
Индекс = Города.ВГраница();
Пока Индекс >= 0 Цикл
Если СтрНачинаетсяС(Города[Индекс], "Т") Тогда
Города.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Удаление элемента с индексом Индекс не трогает элементы левее него, поэтому следующий шаг читает корректное значение. Удалять внутри Для Каждого нельзя: часть элементов будет пропущена.
Сравнение способов
| Способ | Когда брать | Риск |
|---|---|---|
Массив[Индекс] | известен номер элемента | ошибка при выходе за границы |
Для Каждого | обработать все элементы | индекс недоступен без счётчика |
Для Индекс = 0 По ВГраница() | нужен номер или соседний элемент | выход за край, если взять Количество() |
Пока с конца | удаление элементов при обходе | забыть уменьшить счётчик — бесконечный цикл |
Найти() | проверить наличие значения | медленно на больших массивах |
Поиск значения в массиве 1С
Метод Найти() ищет элемент и возвращает его индекс. Если совпадения нет — Неопределено:
Индекс = Города.Найти("Казань");
Если Индекс <> Неопределено Тогда
Сообщить("Нашли на позиции " + Формат(Индекс, "ЧН=0; ЧГ=0"));
Иначе
Сообщить("Такого города нет");
КонецЕсли;
Не пишите Если Города.Найти("Москва") Тогда. Если значение стоит первым, метод вернёт 0, и условие посчитается ложным. Если значения нет, вернётся Неопределено, и платформа выдаст ошибку преобразования к типу «Булево». Сравнивайте результат только с Неопределено.
Как Найти() сравнивает значения:
- 🔢 числа, строки, даты, булево — по значению; строки с учётом регистра, «москва» и «Москва» — разные;
- 🔗 ссылки на справочники и документы — по ссылке, это работает надёжно;
- 📦 структуры, массивы, таблицы — как объекты: две структуры с одинаковыми полями считаются разными.
Поиск в массиве структур по полю
Если в массиве лежат структуры, Найти() не поможет — нужен перебор:
Функция НайтиПоПолю(Массив, ИмяПоля, Значение)
Для Каждого Элемент Из Массив Цикл
Если Элемент.Свойство(ИмяПоля) И Элемент[ИмяПоля] = Значение Тогда
Возврат Элемент;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
Свойство() защищает от ошибки, если в какой-то структуре нет нужного поля. Обращение Элемент[ИмяПоля] к структуре по имени ключа допустимо.
Поиск без учёта регистра
Для строк без учёта регистра приведите обе стороны к одному виду:
Искомое = НРег("москва");
Для Индекс = 0 По Города.ВГраница() Цикл
Если НРег(Города[Индекс]) = Искомое Тогда
Найден = Индекс;
Прервать;
КонецЕсли;
КонецЦикла;
Прервать выходит из цикла сразу после первого совпадения — дальше проверять незачем.
Вложенные массивы и двумерные структуры
Массив может хранить другие массивы. Так получаются таблицы «строка × столбец». Конструктор принимает размеры по каждому измерению:
Матрица = Новый Массив(2, 3); // 2 строки, в каждой 3 элемента
Матрица[0][0] = "А1";
Матрица[1][2] = "В3";
Значение = Матрица[1][2]; // "В3"
Первый индекс выбирает вложенный массив, второй — элемент внутри него. Все незаполненные ячейки содержат Неопределено.
Перебор двумерного массива — двумя циклами, у каждого своя граница:
Для Стр = 0 По Матрица.ВГраница() Цикл
СтрокаМатрицы = Матрица[Стр];
Для Кол = 0 По СтрокаМатрицы.ВГраница() Цикл
Ячейка = СтрокаМатрицы[Кол];
// обработка ячейки
КонецЦикла;
КонецЦикла;
Внутреннюю границу берите от вложенного массива СтрокаМатрицы.ВГраница(), а не от внешнего. Вложенные массивы могут быть разной длины, если их собирали через Добавить().
Массив структур вместо двумерного массива
На практике удобнее массив структур: поле читается по имени, а не по номеру столбца.
Товары = Новый Массив;
Товары.Добавить(Новый Структура("Наименование, Цена", "Бумага А4", 450));
Товары.Добавить(Новый Структура("Наименование, Цена", "Картридж", 3200));
Цена = Товары[1].Цена; // 3200
Если данных много и нужны отборы, группировки и сортировки, подумайте о таблице значений: у неё есть НайтиСтроки() и Сортировать().
Ошибки при чтении массива и как их ловить
Основные ошибки при работе с массивом возникают из-за границ, типа значения или пустого результата поиска.
Попытка … Исключение
Если массив приходит извне (из обработки загрузки, из HTTP-сервиса, от пользователя) и проверить всё заранее сложно, оберните чтение в попытку:
Попытка
Значение = ДанныеЗагрузки[НомерКолонки];
Исключение
Значение = Неопределено;
ЗаписьЖурналаРегистрации("Загрузка данных",
УровеньЖурналаРегистрации.Предупреждение, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
💡 Не используйте попытку вместо проверки границ в обычном коде. Явная проверка Индекс <= Массив.ВГраница() читается понятнее и работает быстрее. Попытку оставьте для данных, структуру которых вы не контролируете.
ПодробноеПредставлениеОшибки() доступна в платформе 8.3.17 и новее. В более ранних версиях используйте ОписаниеОшибки().
Скорость: когда массив заменить другой коллекцией
Чтение по индексу выполняется мгновенно при любом размере массива. Медленным бывает поиск: Найти() просматривает элементы по порядку, пока не встретит совпадение.
Пример с цифрами. Нужно для 10 000 строк документа проверить, входит ли номенклатура в список из 5 000 позиций. Через Найти() это до 50 миллионов сравнений. Через Соответствие — 10 000 обращений по ключу.
// Один раз строим индекс
Справочник = Новый Соответствие;
Для Каждого Позиция Из СписокПозиций Цикл
Справочник.Вставить(Позиция, Истина);
КонецЦикла;
// Потом проверяем быстро
Для Каждого СтрокаТЧ Из Товары Цикл
Если Справочник[СтрокаТЧ.Номенклатура] <> Неопределено Тогда
// позиция есть в списке
КонецЕсли;
КонецЦикла;
Ещё несколько приёмов:
- ⚡ не вызывайте запрос внутри цикла по массиву ссылок — передайте весь массив параметром в запрос с условием
В (&Список); - 🧹
Количество()в условии циклаПокавызывается на каждом шаге; для больших массивов сохраните его в переменную, если массив не меняется; - 📦 при передаче массива между клиентом и сервером передавайте только нужные данные — каждый вызов сервера сериализует весь массив;
- 🔒 если массив только читают, сделайте
Новый ФиксированныйМассив(Массив)— случайно изменить его не получится.
Не меняйте массив внутри Для Каждого по нему же — ни удалением, ни добавлением. Результат будет непредсказуемым, а ошибка проявится не сразу.
Частые вопросы
Как взять первый элемент массива в 1С?
Массив[0] — но только после проверки Массив.Количество() > 0. Иначе на пустом массиве будет ошибка выхода за границы.Чем ВГраница() отличается от Количество()?
Количество() — число элементов, ВГраница() — индекс последнего. Для массива из пяти элементов это 5 и 4, для пустого — 0 и -1.Можно ли получить элемент массива по значению, а не по индексу?
Найти(Значение), затем читайте Массив[Индекс]. Если нужен доступ по ключу постоянно, используйте Соответствие или Структура.Как получить из массива уникальные значения?
Новый.Найти(Элемент) = Неопределено. Для больших объёмов быстрее собрать ключи в Соответствие.Как превратить массив строк в одну строку?
СтрСоединить(Массив, ", ") склеит элементы через разделитель. Она работает с массивом строк в платформе 8.3.6 и новее.