СписокЗначений: универсальная коллекция для выбора и отметок

· 9 мин

Содержание

Список значений в 1С 8.3 — это универсальная коллекция, которая живёт только в памяти во время сеанса. Создаётся одной строкой Новый СписокЗначений, а каждый её элемент хранит сразу четыре свойства: значение, представление, пометку и картинку.

Частая путаница — приравнять список значений к массиву. Разница принципиальная: элемент списка это отдельный объект ЭлементСпискаЗначений, у которого помимо самого значения есть видимый текст (представление) и флажок-пометка. Поэтому список удобен там, где пользователю надо выбрать вариант или отметить галочками несколько строк, а массив остаётся просто набором значений без оформления.

Работает список и на клиенте, и на сервере, его можно передавать между контекстами и назначать типом реквизита формы. А вот в реквизите справочника или документа напрямую он не хранится — для этого значение упаковывают в ХранилищеЗначения.

Что такое СписокЗначений и где он пригодится

Объект представляет собой упорядоченный набор элементов. У каждого элемента четыре свойства:

  • 🔹 Значение — любое значение любого типа (ссылка, число, строка, дата, даже другой список);
  • 🔹 Представление — строка, которую видит пользователь вместо значения;
  • 🔹 Пометка — булево, галочка «отмечен / не отмечен»;
  • 🔹 Картинка — объект Картинка для иконки слева от строки.

Типичные сценарии, где список незаменим:

  • список выбора у поля ввода (свойство СписокВыбора), когда вариантов немного и справочник заводить незачем;
  • интерактивный выбор одного значения из вариантов через ВыбратьЭлемент;
  • отметка галочками нескольких позиций через ОтметитьЭлементы;
  • хранение отбора или набора ссылок, которые нужно пронести через несколько процедур;
  • параметры и отборы в отчётах на СКД.

Отдельно стоит отметить отборы в системе компоновки данных. Когда в отчёте настраиваете условие вида «в списке» или «не в списке», значением такого отбора как раз выступает список значений — туда складываются все варианты, по которым идёт отбор. То же касается параметров отчётов, принимающих набор значений.

Если данные многоколоночные (номенклатура + количество + цена), список не подойдёт — нужна ТаблицаЗначений. Список хранит ровно одно значение на элемент плюс его оформление.

Создание списка значений: четыре рабочих способа

Пустой список создаётся конструктором:

Список = Новый СписокЗначений;

Дальше его наполняют одним из способов.

1. Поэлементно методом Добавить. Метод принимает до четырёх параметров и возвращает созданный элемент:

Список = Новый СписокЗначений;
Список.Добавить("ПРИХОД", "Приход");
Список.Добавить("РАСХОД", "Расход", Истина);
Список.Добавить(0, "Не задано");

Второй параметр — представление, третий — пометка, четвёртый — картинка. Все кроме первого необязательны.

2. Массивом через ЗагрузитьЗначения. Если значения уже собраны в массив, список заполняется одной командой:

Массив = Новый Массив;
Массив.Добавить("Январь");
Массив.Добавить("Февраль");
Список = Новый СписокЗначений;
Список.ЗагрузитьЗначения(Массив);

При этом представления берутся из строкового вида значений, а прежнее содержимое списка затирается.

3. Копией существующего списка. Метод Скопировать создаёт независимую копию — изменения в ней не затронут оригинал:

КопияСписка = Список.Скопировать();

4. Как свойство поля ввода. Для быстрого списка выбора у реквизита формы не нужен отдельный справочник: наполняете СписокВыбора поля, и пользователь выбирает из выпадающего набора.

ЭлементыФормы.Поле.СписокВыбора.Добавить("Нал", "Наличные");
ЭлементыФормы.Поле.СписокВыбора.Добавить("Безнал", "Безналичные");

Элементы списка: добавить, изменить, удалить, очистить

Добавление в конец — метод Добавить. Вставка в заданную позицию — метод Вставить, первым параметром идёт индекс:

Список.Вставить(0, "СРОЧНО", "Срочные");

Обращение к элементу — по индексу через оператор [] или метод Получить. Индексация с нуля:

Первый = Список[0];
Третий = Список.Получить(2);

Изменение выполняется через свойства полученного элемента — отдельного метода «заменить» нет:

Элемент = Список[0];
Элемент.Представление = "Новый текст";
Элемент.Пометка = Истина;

Количество элементов возвращает метод Количество:

Всего = Список.Количество();

Удаление — метод Удалить, которому передают либо сам элемент, либо его индекс. Полная очистка — метод Очистить:

Список.Удалить(Список[0]);  // по элементу
Список.Удалить(2);          // по индексу
Список.Очистить();          // убрать всё

При удалении в цикле индексы «съезжают»: после удаления элемента все следующие сдвигаются на одну позицию. Прямой цикл Для ... По ... пропустит часть строк. Коллекцию всегда обходят с конца к началу.

Правильный обход с удалением отмеченных строк:

Сч = Список.Количество() - 1;
Пока Сч >= 0 Цикл
    Если Список[Сч].Пометка Тогда
        Список.Удалить(Сч);
    КонецЕсли;
    Сч = Сч - 1;
КонецЦикла;

Оператор Для в 1С умеет только увеличивать счётчик, поэтому для обратного прохода используется Пока с уменьшением переменной.

Поиск, сортировка, выгрузка: приёмы, которые экономят код

Поиск по значению — метод НайтиПоЗначению. Возвращает найденный элемент или Неопределено, поэтому результат проверяют именно на Неопределено:

Найденный = Список.НайтиПоЗначению("РАСХОД");
Если Найденный <> Неопределено Тогда
    Сообщить(Найденный.Представление);
КонецЕсли;

Индекс элемента возвращает метод Индекс, если нужно узнать позицию уже найденного элемента.

Сортировка — два метода: СортироватьПоЗначению и СортироватьПоПредставлению. Направление задаётся значением системного перечисления:

Список.СортироватьПоПредставлению(НаправлениеСортировки.Возр);
Список.СортироватьПоЗначению(НаправлениеСортировки.Убыв);

После сортировки индексы элементов меняются. Если вы где-то запомнили номер строки до сортировки — он станет неверным. Храните ссылку на сам элемент, а не его индекс.

Выгрузка значений в массив — метод ВыгрузитьЗначения. Удобно, когда дальше нужен именно массив:

Массив = Список.ВыгрузитьЗначения();

Частый приём — отдать выгруженный массив в параметр запроса, чтобы отобрать документы по набору ссылок:

Запрос.УстановитьПараметр("Товары", Список.ВыгрузитьЗначения());
// в тексте запроса: ГДЕ Таблица.Номенклатура В (&Товары)

Перемещение элемента — метод СдвинутьЭлемент со смещением (отрицательное — вверх, положительное — вниз).

Устойчивая ссылка на элемент — методы ПолучитьИдентификатор и НайтиПоИдентификатору. Идентификатор не меняется при сортировке, поэтому его удобно передавать между процедурами, когда индекс ненадёжен:

Ид = Список[0].ПолучитьИдентификатор();
// ... позже, даже после сортировки ...
Элемент = Список.НайтиПоИдентификатору(Ид);

Интерактивный выбор на клиенте: ВыбратьЭлемент открывает окно выбора одного значения, ОтметитьЭлементы — окно с галочками. В современном коде под управляемыми формами применяют асинхронные версии ПоказатьВыборЭлемента и ПоказатьОтметкуЭлементов с описанием оповещения.

Где чаще всего ошибаются со списком значений

🔸 Путают элемент и значение. НайтиПоЗначению и Получить возвращают объект ЭлементСпискаЗначений, а не само значение. Чтобы получить значение, обращаются к свойству .Значение.

🔸 Удаляют в прямом цикле. Про сдвиг индексов уже сказано выше — это причина, по которой часть строк «не удаляется».

🔸 Ждут автосохранения в базе. Список живёт только в памяти. Чтобы сохранить его в реквизите объекта, значение оборачивают в ХранилищеЗначения при записи и распаковывают методом Получить при чтении.

🔸 Обращаются к индексу за границей. Попытка взять Список[5], когда в списке три элемента, вызывает ошибку «Индекс находится за границами массива». Перед обращением проверяйте Количество.

🔸 Забывают про представление. Если представление не заполнено, в окне выбора пользователь увидит системное строковое представление значения — иногда это длинная ссылка вида Справочник.Номенклатура: .... Для читаемости представление задают явно.

🔸 Пытаются сравнить два списка оператором равенства. Список1 = Список2 сравнивает ссылки на объекты, а не их содержимое. Два списка с одинаковыми элементами такое сравнение посчитает разными. Сравнивать нужно поэлементно или через выгруженные массивы.

🔸 Ждут массовой пометки одной командой. Отдельного «отметить все» у списка нет. Выставить или снять пометки у всех элементов можно только циклом Для Каждого с присвоением свойства Пометка.

Проверьте себя
Что вернёт метод `НайтиПоЗначению`, если значение найдено?
Как правильно обойти список при удалении элементов?
Где хранится список значений между сеансами?

Передача списка между клиентом и сервером

Список значений — передаваемый тип. Его можно вернуть из серверной процедуры на клиент и передать обратно без ручной сериализации. Это главный плюс в клиент-серверной работе: данные собирают запросом на сервере, а выбор пользователь делает на клиенте.

&НаСервере
Функция ПолучитьСписокНаСервере()
    Список = Новый СписокЗначений;
    // ... наполнение запросом ...
    Возврат Список;
КонецФункции

&НаКлиенте
Процедура ЗаполнитьВыбор(Команда)
    Список = ПолучитьСписокНаСервере();
    // дальше работаем со списком на клиенте
КонецПроцедуры

Помните про разделение контекстов:

  • методы наполнения и обработки (Добавить, ЗагрузитьЗначения, СортироватьПоЗначению, НайтиПоЗначению) работают и на клиенте, и на сервере;
  • интерактивные методы (ВыбратьЭлемент, ОтметитьЭлементы и их асинхронные версии) — только на клиенте, им нужен пользователь и окно.

Список можно назначить типом реквизита управляемой формы — тогда он доступен в обоих контекстах без явной передачи и живёт, пока открыта форма. Это стандартный приём для хранения набора выбора или отбора.

СписокЗначений или ТаблицаЗначений: что выбрать

Обе коллекции существуют в памяти, обе доступны на клиенте и сервере, обе напрямую не хранятся в реквизитах объектов. Выбор зависит от структуры данных.

ПризнакСписокЗначенийТаблицаЗначений
Данные на элементодно значение + представление + пометка + картинкапроизвольное число типизированных колонок
Готовое оформлениеда (представление, галочка, иконка)нет, только значения в колонках
Интерактивный выборВыбратьЭлемент, ОтметитьЭлементычерез динамический список или форму
Когда использоватьвыбор варианта, отметки, набор ссылоктабличные данные, расчёты по строкам

Короткое правило: нужны несколько колонок на строку — берите таблицу значений; нужен выбор или галочки по одному значению — список.

Списки значений легко превращаются в массив (ВыгрузитьЗначения) и загружаются обратно (ЗагрузитьЗначения), поэтому их часто используют как «переносчик» набора ссылок между процедурами, а уже на месте перекладывают в таблицу.

Пример: заполняем список из запроса к базе

Распространённая задача — динамически собрать список из данных базы, чтобы предложить пользователю выбор. Соберём список активной номенклатуры: в значение положим ссылку, в представление — наименование.

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Номенклатура.Ссылка КАК Ссылка,
|    Номенклатура.Наименование КАК Наименование
|ИЗ
|    Справочник.Номенклатура КАК Номенклатура
|ГДЕ
|    НЕ Номенклатура.ПометкаУдаления
|    И НЕ Номенклатура.ЭтоГруппа
|УПОРЯДОЧИТЬ ПО
|    Номенклатура.Наименование";

Выборка = Запрос.Выполнить().Выбрать();

Список = Новый СписокЗначений;
Пока Выборка.Следующий() Цикл
    Список.Добавить(Выборка.Ссылка, Выборка.Наименование);
КонецЦикла;

Теперь Список содержит ссылки в значениях и наименования в представлениях. Его можно отдать в СписокВыбора поля ввода или открыть диалог выбора:

ВыбранныйЭлемент = Список.ВыбратьЭлемент("Выберите товар");
Если ВыбранныйЭлемент <> Неопределено Тогда
    ВыбраннаяНоменклатура = ВыбранныйЭлемент.Значение;
КонецЕсли;

Если список большой (тысячи позиций), не грузите всё в память списком значений — для таких объёмов используют динамический список на форме с отбором. Список значений хорош для десятков, максимум сотен вариантов.

Финальный штрих — отметить галочками несколько строк и собрать отмеченные значения в массив:

Результат = Новый Массив;
Для Каждого Элемент Из Список Цикл
    Если Элемент.Пометка Тогда
        Результат.Добавить(Элемент.Значение);
    КонецЕсли;
КонецЦикла;

Обратите внимание: здесь обход через Для Каждого безопасен, потому что мы ничего не удаляем, а только читаем.

Частые вопросы о списке значений

Можно ли хранить в списке значения разных типов?
Да. В одном списке могут лежать строки, числа, даты и ссылки одновременно — тип значения не ограничен. Это отличает список от типизированной колонки таблицы значений.
Чем отличается Значение от Представления у элемента?
Значение — это то, что программа использует в логике (например, ссылка на товар). Представление — текст, который видит пользователь. Если представление пустое, показывается строковый вид значения.
Как сохранить список значений в реквизите справочника или документа?
Напрямую нельзя. При записи оберните список в ХранилищеЗначения и положите в реквизит соответствующего типа, а при чтении распакуйте методом Получить этого хранилища.
Почему при удалении в цикле удаляются не все нужные элементы?
После удаления элемента индексы последующих сдвигаются, и прямой цикл их пропускает. Обходите коллекцию с конца к началу циклом Пока.
Как получить из списка обычный массив значений?
Методом ВыгрузитьЗначения — он возвращает массив, в котором лежат значения всех элементов без представлений и пометок.