Чтение элементов массива во встроенном языке 1С:Предприятия 8.3 на примерах

· 9 мин

Содержание

Чтобы получить значение из массива в 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 и новее.