Универсальные коллекции 1С 8.3: Массив, Структура, Соответствие, СписокЗначений и ТаблицаЗначений на практике

· 9 мин

Содержание

Коллекция значений в 1С — это не отдельный тип, а общее название объектов, которые хранят набор элементов и умеют отдавать их в цикле Для каждого … Из … Цикл. Самые ходовые универсальные коллекции во встроенном языке 1С 8.3 — Массив, Структура, Соответствие, СписокЗначений, ТаблицаЗначений и ДеревоЗначений. Как с ними работать, зависит от того, по чему вы ищете элемент: по номеру, по имени или по произвольному ключу.

Короткая шпаргалка выбора: нужен упорядоченный набор по номеру — Массив. Нужны именованные поля — Структура. Нужен быстрый поиск по ключу любого типа — Соответствие. Нужен выбор пользователем из списка с представлением и пометкой — СписокЗначений. Нужны строки и колонки, итоги и группировки — ТаблицаЗначений.

Что такое коллекция значений и какие типы к ней относятся

Строки Новый КоллекцияЗначений() в 1С не существует. Если написать её в модуле, синтаксис-помощник ничего не подскажет, а при выполнении будет ошибка Тип не определен (КоллекцияЗначений). «Коллекция» — это свойство типа, а не сам тип.

Объект считается коллекцией, если у него есть три признака:

  • 🔁 его можно перебрать циклом Для каждого;
  • 🔢 у него есть метод Количество();
  • 📍 к элементам можно обратиться через квадратные скобки [ ] — по индексу или по ключу.

Универсальные коллекции создаются конструктором Новый и живут только в памяти. Есть и «служебные» коллекции, которые вы не создаёте сами, а получаете от других объектов: колонки таблицы (КоллекцияКолонокТаблицыЗначений), строки дерева (КоллекцияСтрокДереваЗначений), табличная часть документа, данные формы (ДанныеФормыКоллекция). Методы у них похожи, но набор зависит от владельца.

Фиксированные варианты

У Массив, Структура и Соответствие есть неизменяемые двойники: ФиксированныйМассив, ФиксированнаяСтруктура, ФиксированноеСоответствие. Добавить или удалить элемент в них нельзя, только прочитать.

Где они обязательны на практике:

  • 🧩 в параметрах сеанса — туда коллекцию кладут только в фиксированном виде;
  • ♻️ в функциях общих модулей с повторным использованием возвращаемых значений: возвращённое значение кэшируется, и обычный массив вызывающий код мог бы случайно изменить для всех последующих вызовов.

Превратить фиксированный массив обратно в обычный можно конструктором: Новый Массив(ФиксированныйМассив).

Как создать коллекцию и сразу её заполнить

Все универсальные коллекции создаются через Новый. Отличаются только параметры конструктора.

// Пустой массив и массив из трёх элементов со значением Неопределено
Массив1 = Новый Массив;
Массив2 = Новый Массив(3);

// Структура: ключи перечисляются через запятую, значения — следом
Реквизиты = Новый Структура("Организация, Дата, Сумма", Организация, ТекущаяДата(), 0);

// Соответствие и список значений создаются пустыми
Цены = Новый Соответствие;
Выбор = Новый СписокЗначений;

// Таблица значений: сначала колонки, потом строки
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура");
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));

У Новый Массив(3) элементы уже есть, просто они равны Неопределено. Если после такого создания вызвать Добавить(), новый элемент встанет четвёртым, с индексом 3. Это частая причина «лишних пустых значений» в начале массива.

Колонке таблицы значений лучше сразу задавать тип через ОписаниеТипов. Без типа колонка примет что угодно, но Итог(), Свернуть() и сортировка по такой колонке ведут себя менее предсказуемо, а при передаче таблицы в запрос платформа не поймёт, какой тип у поля.

Ещё один источник готовых коллекций — запрос. Запрос.Выполнить().Выгрузить() возвращает ТаблицаЗначений, а ТЗ.ВыгрузитьКолонку("Номенклатура") — Массив значений одной колонки.

Добавление, поиск и удаление элементов в коллекциях

Ниже — методы, которые нужны каждый день. Названия проверены по синтаксис-помощнику платформы 8.3.

Массив

ДействиеМетодЧто вернёт
Добавить в конецМассив.Добавить(Значение)—
Вставить по индексуМассив.Вставить(Индекс, Значение)—
НайтиМассив.Найти(Значение)индекс или Неопределено
УдалитьМассив.Удалить(Индекс)—
Последний индексМассив.ВГраница()число, у пустого -1
ОчиститьМассив.Очистить()—

Индексы начинаются с нуля. У массива из пяти элементов последний индекс — 4, а ВГраница() вернёт 4.

Структура

Реквизиты.Вставить("Комментарий", "Оплата по счёту 15");
Если Реквизиты.Свойство("Сумма") Тогда
    Реквизиты.Сумма = 1500;
КонецЕсли;
Реквизиты.Удалить("Комментарий");

Ключ структуры — это имя: он должен подходить под правила идентификатора (буквы, цифры, подчёркивание, не начинаться с цифры). Регистр ключа не важен: Реквизиты.сумма и Реквизиты.Сумма — одно и то же. Вставить() с существующим ключом не даёт ошибку, а перезаписывает значение.

Свойство(Ключ, Значение) с двумя параметрами сразу кладёт найденное значение во вторую переменную — это быстрее и безопаснее, чем проверка и отдельное обращение через точку.

Соответствие

Цены.Вставить(Номенклатура, 250);
Цена = Цены.Получить(Номенклатура);   // Неопределено, если ключа нет
Цены[ДругаяНоменклатура] = 300;       // присвоение по ключу тоже добавляет пару
Цены.Удалить(Номенклатура);

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

СписокЗначений

У элемента списка четыре свойства: Значение, Представление, Пометка, Картинка. Отсюда и методы:

  • ➕ Добавить(Значение, Представление, Пометка) — добавляет элемент и возвращает его;
  • 🔎 НайтиПоЗначению(Значение) — возвращает элемент списка или Неопределено;
  • 🔃 СортироватьПоЗначению() и СортироватьПоПредставлению();
  • 📤 ВыгрузитьЗначения() — отдаёт Массив, ЗагрузитьЗначения(Массив) — заполняет из него;
  • 🗑 Удалить() принимает либо сам элемент, либо его индекс.

ТаблицаЗначений

НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Номенклатура = Номенклатура;
НоваяСтрока.Количество = 10;

Отбор = Новый Структура("Номенклатура", Номенклатура);
НайденныеСтроки = ТЗ.НайтиСтроки(Отбор);   // Массив строк

ТЗ.Свернуть("Номенклатура", "Количество");
Всего = ТЗ.Итог("Количество");
ТЗ.Сортировать("Количество Убыв");

Свернуть() группирует по колонкам из первого параметра и суммирует колонки из второго. Колонки, которых нет ни в одном параметре, пропадут — это не ошибка, так задумано.

Перебор коллекции: три способа обойти элементы

Цикл «Для каждого»

Подходит для любой коллекции и читается проще всего.

Для каждого Элемент Из Массив1 Цикл
    Сообщить(Элемент);
КонецЦикла;

Что окажется в переменной цикла, зависит от типа:

КоллекцияПеременная цикла
Массивсамо значение
Структура, СоответствиеКлючИЗначение со свойствами Ключ и Значение
СписокЗначенийЭлементСпискаЗначений (берите .Значение)
ТаблицаЗначенийСтрокаТаблицыЗначений
ДеревоЗначенийстроки только верхнего уровня

Для дерева обход всех уровней пишут рекурсией по свойству Строки каждой строки.

Цикл по индексу

Нужен, когда важен номер элемента или вы одновременно обходите две коллекции одинаковой длины.

Для Индекс = 0 По Массив1.ВГраница() Цикл
    Сообщить(СтрШаблон("%1: %2", Индекс, Массив1[Индекс]));
КонецЦикла;

СтрШаблон здесь не случаен: число при склейке строкой через + получит разделитель разрядов, и вместо 1500 пользователь увидит 1 500. Либо СтрШаблон, либо Формат(Число, "ЧГ=0").

Обратный цикл — для удаления

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

Всего = Массив1.Количество();
Для Сч = 1 По Всего Цикл
    Индекс = Всего - Сч;
    Если Массив1[Индекс] = Неопределено Тогда
        Массив1.Удалить(Индекс);
    КонецЕсли;
КонецЦикла;

Граница цикла посчитана один раз до начала, а индекс идёт от последнего элемента к нулевому. Удаление хвоста не сдвигает элементы, до которых цикл ещё не дошёл. Для таблицы значений то же самое: сначала соберите строки в массив через НайтиСтроки(), потом удаляйте их, перебирая этот массив.

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

ТипДоступ к элементуКлючПорядокДоступен на клиентеТипичная задача
Массив[Индекс]номер с 0сохраняетсядасписок ссылок для запроса, набор значений
Структура.Имя или ["Имя"]строка-идентификаторне опирайтесьдапараметры процедуры, набор реквизитов
Соответствие[Ключ], Получить()любой значимый типне опирайтесьдакэш, справочник «ключ → значение»
СписокЗначений[Индекс], НайтиПоЗначению()номер с 0сохраняетсядавыбор пользователем, отметки
ТаблицаЗначений[Индекс], поиск по колонкамномер строкисохраняетсянет, только серверрасчёты, свёртка, итоги

Главное отличие массива от списка значений — у элемента списка есть представление и пометка. Если пользователю ничего не показывают и не просят отметить — берите Массив: он проще и легче.

Чем Структура отличается от Соответствие: ключ структуры — только имя по правилам идентификатора, зато к значению можно обратиться через точку. Ключ соответствия — что угодно, включая ссылку на справочник, но точки нет, только [ ] или Получить().

ТаблицаЗначений и ДеревоЗначений на клиенте не существуют. На форме вместо них реквизит типа ДанныеФормыКоллекция или ДанныеФормыДерево. Переводят туда и обратно методами формы ЗначениеВРеквизитФормы() и РеквизитФормыВЗначение() — на сервере.

Какую коллекцию выбрать?

Где коллекции значений нужны в реальной разработке

Параметр запроса. Условие ГДЕ Номенклатура В (&Список) принимает Массив, СписокЗначений или ТаблицаЗначений. Передать таблицу как временную таблицу можно конструкцией ВЫБРАТЬ … ПОМЕСТИТЬ ВТ ИЗ &ТЗ КАК Т: у колонок такой таблицы обязательно должны быть заданы типы, иначе запрос упадёт.

Кэш вместо запроса в цикле. Классическая ошибка производительности — запрос к базе внутри Для каждого по строкам документа. Правильно: один запрос до цикла, результат в Соответствие, в цикле — Получить(). На документе в 500 строк это 1 обращение к базе вместо 500.

Параметры процедур. Процедура с семью параметрами неудобна: при добавлении восьмого нужно править все вызовы. Передача Структура решает это, а Свойство() позволяет сделать параметр необязательным.

Обмен с сервером. Из клиентского кода на сервер и обратно свободно ходят Массив, Структура, Соответствие, СписокЗначений с примитивами и ссылками внутри. Таблицу значений так передать нельзя — её разбирают в массив структур или кладут в реквизит формы.

Готовые функции БСП. В типовых конфигурациях на БСП не пишите велосипеды: в общем модуле ОбщегоНазначенияКлиентСервер есть функции для массивов — например, СвернутьМассив() (убрать дубли) и ДополнитьМассив() (добавить элементы другого массива, при желании только уникальные). Набор функций зависит от версии БСП, проверьте его в модуле своей конфигурации.

Ошибки при работе с коллекциями, которые встречаются чаще других

  • ❌ Присвоение вместо копии. Массив2 = Массив1 не создаёт новый массив, обе переменные указывают на один объект. Изменили второй — изменился и первый. Для таблицы есть ТЗ.Скопировать(), для массива — новый массив и Для каждого с Добавить().
  • ❌ Обращение к несуществующему ключу структуры. Реквизиты.Склад при отсутствии ключа даёт Поле объекта не обнаружено (Склад). Проверяйте Свойство().
  • ❌ Выход за границу массива. Обращение к Массив1[5] у массива из пяти элементов даёт ошибку Значение индекса выходит за пределы диапазона. Помните: последний индекс — Количество() - 1.
  • ❌ Ключ структуры с пробелом или с цифры. Новый Структура("1Сумма") вызовет ошибку: ключ не является идентификатором. Для таких ключей — Соответствие.
  • ❌ Сравнение структур через «=». Две структуры с одинаковым содержимым не равны: сравниваются объекты, а не значения внутри. По этой же причине Массив.Найти(Структура) найдёт только тот самый экземпляр.
Есть ли в 1С тип КоллекцияЗначений?
Нет. Это общее название для объектов, которые можно перебрать циклом «Для каждого». Конструктор «Новый КоллекцияЗначений» вызовет ошибку «Тип не определен».
Можно ли хранить коллекцию в реквизите справочника или документа?
Напрямую нет. Для этого есть тип реквизита ХранилищеЗначения: коллекцию помещают в Новый ХранилищеЗначения(Значение), а читают методом Получить(). Для отбора и поиска такие данные непригодны.
Можно ли вложить одну коллекцию в другую?
Да. Массив структур, соответствие с массивами в значениях, структура с таблицей внутри — обычная практика. Следите только за тем, чтобы на клиент не уходили таблицы и деревья значений.
Как быстро превратить список значений в массив и обратно?
Метод ВыгрузитьЗначения() у списка возвращает массив, а ЗагрузитьЗначения(Массив) заполняет список из массива. Представления и пометки при этом не переносятся.
Как сохранить коллекцию в строку или файл?
Через XDTO-сериализацию или функции ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр(). Для обмена с внешними системами удобнее JSON: ЗаписатьJSON() работает со Структурой, Соответствием со строковыми ключами и Массивом.