Разработка и Конфигурация · 11 мин
1С: как обойти список значений
Как обойти список значений в 1С: циклы Для Каждого и Для, обращение к элементам по индексу, поиск и удаление. Примеры кода, типичные ошибки и FAQ.
Список значений в 1С — это универсальная коллекция, в которой каждый элемент хранит четыре свойства: само значение, представление, пометку (флажок) и картинку. Обход такого списка требуется постоянно: при обработке выбранных пользователем строк, при формировании параметров запроса, при переборе помеченных элементов формы. Встроенный язык даёт несколько способов перебора, и у каждого есть свои ограничения.
Основные варианты обхода — цикл Для Каждого … Из … Цикл и классический цикл Для … По … Цикл с обращением по индексу. Первый удобен для чтения, второй незаменим, когда нужен номер элемента или требуется модифицировать список «на месте» с учётом позиций. Отдельный сценарий — перебор с удалением элементов: здесь прямой цикл опасен, и список обходят в обратном порядке.
Ниже разобраны все рабочие способы с примерами кода, приёмы поиска и удаления, а также ошибки, которые чаще всего приводят к исключениям «Индекс находится за границами массива» и к пропуску элементов.
Что представляет собой список значений и как к нему обращаться
Объект типа СписокЗначений создаётся конструктором Новый СписокЗначений либо приходит готовым — например, как реквизит формы или результат метода ВыгрузитьЗначения() таблицы значений. Каждый элемент списка — это объект со свойствами Значение, Представление, Пометка и Картинка.
Количество элементов возвращает метод Количество(). Обращение к конкретному элементу возможно по индексу через квадратные скобки или метод Получить(Индекс). Важно помнить: нумерация элементов начинается с нуля, то есть первый элемент — Список[0], а последний — Список[Список.Количество() - 1].
Попытка обратиться к несуществующему индексу вызывает исключение, поэтому перед обращением по индексу всегда проверяйте, что список не пуст, а индекс находится в допустимых границах.
Обход списка циклом «Для Каждого»
Цикл Для Каждого — рекомендуемый способ, когда нужно просто прочитать все элементы. Он короче, читаемее и исключает ошибки с индексами:
Для Каждого Элемент Из СписокЗначений Цикл
Сообщить(Элемент.Значение);
КонецЦикла;
Внутри цикла переменная Элемент содержит ссылку на текущий элемент списка, поэтому доступны все его свойства. Типовой сценарий — обработать только помеченные строки:
Для Каждого Элемент Из СписокЗначений Цикл
Если Элемент.Пометка Тогда
ОбработатьДокумент(Элемент.Значение);
КонецЕсли;
КонецЦикла;
У цикла «Для Каждого» есть ограничение: внутри него нельзя удалять или добавлять элементы в обходимый список — это приводит к непредсказуемому поведению или ошибке. Для таких задач используйте индексный цикл.
Обход по индексу циклом «Для»
Когда нужен номер элемента — например, для вывода нумерованного списка или синхронного обхода двух списков, — применяется классический цикл со счётчиком:
Для Индекс = 0 По СписокЗначений.Количество() - 1 Цикл
Элемент = СписокЗначений[Индекс];
Сообщить(Строка(Индекс + 1) + ". " + Элемент.Представление);
КонецЦикла;
Обратите внимание на верхнюю границу: Количество() - 1. Это самая частая ошибка новичков — написать По СписокЗначений.Количество() и получить исключение на последней итерации, потому что индексация идёт с нуля.
Также допустим вариант с методом Получить(): Элемент = СписокЗначений.Получить(Индекс); — по смыслу он эквивалентен обращению через квадратные скобки.
Для Индекс = 0 По СписокЗначений.Количество() - 1 Цикл
Элемент = СписокЗначений[Индекс];
// работа с Элемент.Значение, Элемент.Пометка, Элемент.Представление
КонецЦикла;Обход с удалением элементов: только с конца
Если удалять элементы в прямом цикле, индексы оставшихся элементов сместятся, и часть строк будет пропущена. Правильный приём — обход от последнего элемента к первому:
Для Индекс = 1 - СписокЗначений.Количество() По 0 Цикл
РеальныйИндекс = -Индекс;
Если Не СписокЗначений[РеальныйИндекс].Пометка Тогда
СписокЗначений.Удалить(РеальныйИндекс);
КонецЕсли;
КонецЦикла;
Альтернатива — цикл Пока с ручным управлением индексом: не увеличивать счётчик после удаления, потому что на место удалённого элемента встал следующий:
Индекс = 0;
Пока Индекс < СписокЗначений.Количество() Цикл
Если Не СписокЗначений[Индекс].Пометка Тогда
СписокЗначений.Удалить(Индекс);
Иначе
Индекс = Индекс + 1;
КонецЕсли;
КонецЦикла;
Третий безопасный вариант — не удалять, а собрать новый список: создать второй СписокЗначений и переложить в него нужные элементы обычным циклом «Для Каждого». Это чуть дороже по памяти, но код проще и не содержит риска смещения индексов.
- 1Определите условие удаления (например, Элемент.Пометка = Ложь)
- 2Организуйте обход от конца списка к началу или цикл Пока с ручным индексом
- 3Вызывайте СписокЗначений.Удалить(Индекс) только для текущего элемента
- 4После цикла проверьте СписокЗначений.Количество() — оно должно соответствовать ожиданию
Поиск элемента вместо ручного перебора
Часто полный обход вообще не нужен. Если задача — проверить наличие значения, используйте метод НайтиПоЗначению(Значение). Он возвращает найденный элемент или Неопределено:
Найденный = СписокЗначений.НайтиПоЗначению(ИскомаяСсылка);
Если Найденный <> Неопределено Тогда
Найденный.Пометка = Истина;
КонецЕсли;
Для поиска по представлению существует метод НайтиПоПредставлению(Представление), но он возвращает массив всех подходящих элементов, и его результат нужно проверять на пустоту. Поиск по представлению менее надёжен, так как представления могут совпадать у разных значений.
Метод Индекс(Элемент) решает обратную задачу — возвращает позицию известного элемента. Учтите, что все эти методы выполняют линейный поиск: на очень больших списках (десятки тысяч элементов и более) многократный поиск в цикле заметно тормозит. В таких случаях быстрее выгрузить данные в соответствие или таблицу значений с индексом.
Способы обхода и поиска
Чтение и изменение свойств элементов. Не подходит для удаления и когда нужен индекс.
Нужен номер элемента или синхронный обход нескольких списков. Требует аккуратности с границей Количество()-1.
Удаление или вставка элементов во время обхода. Код сложнее, но безопасен.
Разовая проверка наличия значения. Не подходит для поиска по пометке или частичному совпадению.
Обход списка значений на форме и в запросах
Когда список значений является реквизитом формы (например, поле с флажками для выбора нескольких контрагентов), обход выполняется теми же циклами, но помните про контекст: на клиенте недоступны серверные вызовы без обёртки, поэтому тяжёлую обработку результата выносят в серверную процедуру, передавая туда сам список — он сериализуется между клиентом и сервером.
Типовой приём — передать список значений как параметр запроса с оператором В (&Список). Перед этим список полезно «очистить» от непомеченных элементов обходом с конца и от дублей методом СортироватьПоЗначению() с последующим удалением повторов, чтобы запрос работал с компактным набором значений.
Ещё одна частая задача — обход списка, полученного из ТаблицаЗначений.ВыгрузитьКолонку() и последующего ЗагрузитьЗначения(). Здесь помните, что в список могут попасть пустые ссылки и Неопределено, поэтому внутри цикла добавляйте проверку ЗначениеЗаполнено(Элемент.Значение).
Типичные ошибки и как их проверить
Первая ошибка — выход за границу: цикл Для Индекс = 0 По СписокЗначений.Количество() без вычитания единицы. Признак — исключение «Индекс находится за границами массива» на последней итерации, причём на пустом списке ошибка может не проявиться, и дефект уйдёт в рабочую базу.
Вторая ошибка — пропуск элементов при удалении в прямом цикле. Внешне всё «работает», но часть строк остаётся необработанной. Контроль простой: после обхода сравните итоговое Количество() с ожидаемым числом оставшихся элементов.
Третья ошибка — сравнение значений разных типов при поиске. НайтиПоЗначению сравнивает строго: строка «000001» не равна ссылке с таким кодом. Если поиск «не находит» заведомо существующее значение, проверьте типы через ТипЗнч() у искомого значения и элементов списка.
Проверить корректность обхода помогает простая диагностика: выведите в цикле индекс и значение через Сообщить() или используйте отладчик с точкой останова внутри цикла, наблюдая за переменной счётчика и содержимым элемента. Для списков на форме удобно смотреть текущее состояние через табло в отладчике.
Что важно запомнить
Для чтения используйте «Для Каждого», для работы с индексами — «Для» от 0 до Количество()-1, для удаления — обход с конца или цикл «Пока». Для разового поиска применяйте НайтиПоЗначению вместо ручного перебора и всегда проверяйте типы сравниваемых значений.
Частые вопросы
Можно ли обойти список значений запросом?
Напрямую нет — запрос работает с таблицами. Но список можно передать как параметр оператора В (&Список) для отбора, либо сначала преобразовать его в таблицу значений через построчное заполнение и использовать в пакетном запросе как временную таблицу.
Как проверить, что список значений пуст?
Условие Если СписокЗначений.Количество() = 0 Тогда. Отдельного метода «Пустой» у списка нет. Эту проверку стоит делать перед любым обращением по индексу и перед передачей списка в запрос, чтобы не строить заведомо пустой отбор.
Чем список значений отличается от массива при обходе?
Оба обходятся одинаковыми циклами и индексируются с нуля. Различие в содержимом: элемент массива — это просто значение, а элемент списка — объект с Значением, Представлением, Пометкой и Картинкой. Список также можно показывать пользователю диалогами выбора и отметки, массив — нельзя.
Как обойти только помеченные элементы?
Полного отбора «по пометке» у списка нет. Обходите весь список циклом «Для Каждого» и внутри проверяйте Если Элемент.Пометка Тогда. Если помеченные элементы нужны многократно, соберите их один раз в отдельный массив или список и дальше работайте с ним.
Почему НайтиПоЗначению не находит существующий элемент?
Чаще всего не совпадают типы: вы ищете строку, а в списке хранится ссылка (или наоборот). Проверьте типы через ТипЗнч(). Реже причина в том, что значения — объекты разных информационных баз или несравнимые типы вроде хранилища значения.
Что быстрее: «Для Каждого» или цикл по индексу?
Для списка значений разница в типовых задачах незначительна, выбирайте по удобочитаемости. Заметный выигрыш даёт не смена цикла, а отказ от многократного линейного поиска НайтиПоЗначению внутри другого цикла — там помогает соответствие или индексированная таблица значений.