Универсальные коллекции 1С 8.3: Массив, Структура, Соответствие, СписокЗначений и ТаблицаЗначений на практике
Содержание
Коллекция значений в 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Сумма")вызовет ошибку: ключ не является идентификатором. Для таких ключей —Соответствие. - ❌ Сравнение структур через «=». Две структуры с одинаковым содержимым не равны: сравниваются объекты, а не значения внутри. По этой же причине
Массив.Найти(Структура)найдёт только тот самый экземпляр.