Массив в таблицу значений 1С переводят, когда данным нужны колонки: чтобы передать их в запрос и соединить с таблицами базы, свернуть, отсортировать по нескольким полям или загрузить в табличную часть. Ниже три сценария с готовым кодом: массив простых значений в одну колонку методом ЗагрузитьКолонку, массив структур в строки таблицы с автоматическим созданием и типизацией колонок и одна структура в таблицу из одной строки. Отдельно — что для этого есть в БСП и почему без типов колонок таблица не пройдёт в запрос.
Таблица значений доступна на сервере, во внешнем соединении и в толстом клиенте, но не в тонком и веб-клиенте. В управляемом приложении весь код ниже выполняйте на сервере: в общем модуле с флажком «Сервер» или в процедуре формы с директивой &НаСервере. Примеры рассчитаны на платформу 8.3; функция СтрРазделить из первого примера появилась в 8.3.6 и работает при режиме совместимости конфигурации не ниже 8.3.6.
Элементы.Список.ВыделенныеСтроки вернул массив ссылок, а к ним в запросе нужно присоединить остатки или цены.СтрРазделить получился массив строк, который надо сопоставить с номенклатурой.Загрузить() табличной части или вывод на форму.Чтобы загрузить массив в таблицу значений, когда в нём простые значения — строки, числа, ссылки, — используют метод ЗагрузитьКолонку(Массив, Колонка). Главное, что про него надо знать: строк он не добавляет. Метод заполняет колонку в уже существующих строках: первый элемент массива идёт в первую строку, второй во вторую. На пустой таблице вызов проходит без ошибки, и таблица так и остаётся пустой. Поэтому порядок такой: колонки, затем по пустой строке на каждый элемент массива, затем загрузка. Колонку указывают именем, индексом или объектом КолонкаТаблицыЗначений.
// Общий модуль с флажком «Сервер»
// "А-100; Б-2044; В-7" -> таблица с колонкой Артикул (СтрРазделить: 8.3.6+)
Функция ТаблицаАртикулов(СтрокаАртикулов) Экспорт
Артикулы = Новый Массив;
Для Каждого Часть Из СтрРазделить(СтрокаАртикулов, ";", Ложь) Цикл
Артикул = СокрЛП(Часть);
Если Не ПустаяСтрока(Артикул) Тогда
Артикулы.Добавить(Артикул);
КонецЕсли;
КонецЦикла;
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
// ЗагрузитьКолонку строк не создаёт: сначала по строке на каждый элемент
Для Счетчик = 1 По Артикулы.Количество() Цикл
Таблица.Добавить();
КонецЦикла;
Таблица.ЗагрузитьКолонку(Артикулы, "Артикул");
Возврат Таблица;
КонецФункции
Это и есть ответ на вопрос, как превратить массив строк в таблицу значений: одна колонка, тип «Строка» длиной 50 символов, по строке таблицы на каждый артикул. Пустые куски отсеиваются после СокрЛП: из «А-100; ; Б-2044» СтрРазделить вернёт элемент из одного пробела, и третий параметр Ложь его не отбросит — формально он не пустой. Тип колонки нужен для запроса — об этом ниже.
Для одной колонки цикл Таблица.Добавить().Артикул = Артикул делает то же самое за один проход. ЗагрузитьКолонку выигрывает там, где строки уже есть: колонок несколько или таблицу дополняют новой колонкой. В конфигурации на БСП оба шага сразу делает ДополнитьТаблицуИзМассива — о ней ниже.
Если данные лежат в параллельных массивах одинаковой длины (номенклатура отдельно, количества отдельно), создайте строки один раз и загрузите колонки по очереди.
// Номенклатура и Количества - массивы одинаковой длины
Функция ТаблицаТоваров(Номенклатура, Количества) Экспорт
Если Номенклатура.Количество() <> Количества.Количество() Тогда
ВызватьИсключение "Массивы номенклатуры и количеств разной длины";
КонецЕсли;
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Для Счетчик = 1 По Номенклатура.Количество() Цикл
Таблица.Добавить();
КонецЦикла;
Таблица.ЗагрузитьКолонку(Номенклатура, "Номенклатура");
Таблица.ЗагрузитьКолонку(Количества, "Количество");
Возврат Таблица;
КонецФункции
Строки должны существовать до вызова ЗагрузитьКолонку, и их должно быть ровно столько, сколько элементов в каждом массиве. Без проверки длины в начале количества при расхождении массивов окажутся не у той номенклатуры.
Второй частый случай — массив структур с одинаковым набором ключей: Артикул, Количество, Цена. Строку таблицы заполняет ЗаполнитьЗначенияСвойств: он копирует значения из источника в одноимённые колонки приёмника.
// Товары - массив структур с ключами Артикул, Количество, Цена
Функция ТаблицаИзМассиваСтруктур(Товары) Экспорт
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Таблица.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
Для Каждого Товар Из Товары Цикл
ЗаполнитьЗначенияСвойств(Таблица.Добавить(), Товар);
КонецЦикла;
Возврат Таблица;
КонецФункции
Ключи, для которых нет колонки, метод пропускает без ошибки, а колонки без пары в структуре остаются с пустым значением своего типа. Третьим параметром можно ограничить список свойств: ЗаполнитьЗначенияСвойств(НоваяСтрока, Товар, "Артикул, Цена"), четвёртым — перечислить исключения.
Когда набор ключей заранее неизвестен — например, данные пришли из внешнего сервиса, — колонки создают по самим данным. Функции ниже собирают ключи и типы значений всех элементов и строят таблицу с типизированными колонками, готовую для запроса. Разместите их в общем модуле с флажком «Сервер», например ПреобразованиеКоллекций.
// Общий модуль ПреобразованиеКоллекций
// Флажки: Сервер (при необходимости также Внешнее соединение).
// Директивы компиляции не нужны. Вызывать из серверного кода,
// в модуле формы - из процедуры с директивой &НаСервере.
#Область ПрограммныйИнтерфейс
// Создаёт таблицу значений с одной колонкой и загружает в неё массив.
//
// Параметры:
// Массив - Массив - простые значения: строки, числа, даты, ссылки.
// ИмяКолонки - Строка - имя создаваемой колонки.
// ТипКолонки - ОписаниеТипов, Неопределено - если не задан, определяется по значениям.
//
// Возвращаемое значение:
// ТаблицаЗначений
//
Функция МассивВТаблицуЗначений(Массив, ИмяКолонки = "Значение", ТипКолонки = Неопределено) Экспорт
Если ТипКолонки = Неопределено Тогда
ТипКолонки = ОписаниеТиповПоЗначениям(Массив);
КонецЕсли;
Таблица = Новый ТаблицаЗначений;
ДобавитьКолонку(Таблица, ИмяКолонки, ТипКолонки);
Для Счетчик = 1 По Массив.Количество() Цикл
Таблица.Добавить(); // ЗагрузитьКолонку заполняет только существующие строки
КонецЦикла;
Таблица.ЗагрузитьКолонку(Массив, ИмяКолонки);
Возврат Таблица;
КонецФункции
// Преобразует массив структур или соответствий в таблицу значений.
// Колонки создаются по ключам всех элементов, тип колонки - по значениям.
//
// Параметры:
// Элементы - Массив из Структура, ФиксированнаяСтруктура, Соответствие -
// ключи должны быть допустимыми именами колонок.
//
// Возвращаемое значение:
// ТаблицаЗначений
//
Функция МассивСтруктурВТаблицуЗначений(Элементы) Экспорт
ЗначенияКолонок = Новый Соответствие; // ключ в верхнем регистре -> массив значений
ИменаКолонок = Новый Массив; // ключи в порядке первого появления
Для Каждого Элемент Из Элементы Цикл
Для Каждого КлючИЗначение Из Элемент Цикл
// Имена колонок регистронезависимы, ключи соответствия - нет
КлючПоиска = ВРег(КлючИЗначение.Ключ);
Значения = ЗначенияКолонок[КлючПоиска];
Если Значения = Неопределено Тогда
Значения = Новый Массив;
ЗначенияКолонок.Вставить(КлючПоиска, Значения);
ИменаКолонок.Добавить(КлючИЗначение.Ключ);
КонецЕсли;
Значения.Добавить(КлючИЗначение.Значение);
КонецЦикла;
КонецЦикла;
Таблица = Новый ТаблицаЗначений;
Для Каждого ИмяКолонки Из ИменаКолонок Цикл
ДобавитьКолонку(Таблица, ИмяКолонки, ОписаниеТиповПоЗначениям(ЗначенияКолонок[ВРег(ИмяКолонки)]));
КонецЦикла;
Для Каждого Элемент Из Элементы Цикл
НоваяСтрока = Таблица.Добавить();
Для Каждого КлючИЗначение Из Элемент Цикл
НоваяСтрока[КлючИЗначение.Ключ] = КлючИЗначение.Значение;
КонецЦикла;
КонецЦикла;
Возврат Таблица;
КонецФункции
// Преобразует одну структуру в таблицу значений из одной строки.
//
// Параметры:
// Структура - Структура, ФиксированнаяСтруктура, Соответствие
//
// Возвращаемое значение:
// ТаблицаЗначений
//
Функция СтруктураВТаблицуЗначений(Структура) Экспорт
Элементы = Новый Массив;
Элементы.Добавить(Структура);
Возврат МассивСтруктурВТаблицуЗначений(Элементы);
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
// Собирает описание типов по фактическим значениям коллекции.
// Для строк задаёт длину по самой длинной строке, чтобы колонку
// можно было сравнивать и группировать в запросе.
// Возвращает Неопределено, если непустых значений нет.
Функция ОписаниеТиповПоЗначениям(Значения)
Типы = Новый Массив;
ДлинаСтроки = 0;
Для Каждого Значение Из Значения Цикл
Если Значение = Неопределено Или Значение = Null Тогда
Продолжить;
КонецЕсли;
ТекущийТип = ТипЗнч(Значение);
Если Типы.Найти(ТекущийТип) = Неопределено Тогда
Типы.Добавить(ТекущийТип);
КонецЕсли;
Если ТекущийТип = Тип("Строка") Тогда
ДлинаСтроки = Макс(ДлинаСтроки, СтрДлина(Значение));
КонецЕсли;
КонецЦикла;
Если Типы.Количество() = 0 Тогда
Возврат Неопределено;
КонецЕсли;
КвалификаторыСтроки = Новый КвалификаторыСтроки(Макс(ДлинаСтроки, 1), ДопустимаяДлина.Переменная);
Возврат Новый ОписаниеТипов(Типы, , КвалификаторыСтроки);
КонецФункции
Процедура ДобавитьКолонку(Таблица, ИмяКолонки, ТипКолонки)
Если ТипКолонки = Неопределено Тогда
// Тип определить не по чему: колонка произвольного типа, в запрос её не передавайте
Таблица.Колонки.Добавить(ИмяКолонки);
Иначе
Таблица.Колонки.Добавить(ИмяКолонки, ТипКолонки);
КонецЕсли;
КонецПроцедуры
#КонецОбласти
Пример вызова из модуля формы:
// Модуль формы. ТаблицаТоваров - реквизит формы типа ТаблицаЗначений
// с колонками Артикул, Количество, Цена.
&НаСервере
Процедура ЗагрузитьТоварыНаСервере()
Товары = Новый Массив;
Товары.Добавить(Новый Структура("Артикул, Количество, Цена", "А-100", 2, 150.5));
Товары.Добавить(Новый Структура("Артикул, Количество", "Б-2044", 1)); // без цены
Таблица = ПреобразованиеКоллекций.МассивСтруктурВТаблицуЗначений(Товары);
// Колонки: Артикул - Строка(6), Количество - Число, Цена - Число.
// У второй строки Цена = 0: пустое значение числовой колонки.
ЗначениеВРеквизитФормы(Таблица, "ТаблицаТоваров");
КонецПроцедуры
МассивВТаблицуЗначений цикл с Таблица.Добавить() стоит перед ЗагрузитьКолонку: без него функция вернёт таблицу с колонкой, но без строк.ИменаКолонок хранит ключи в порядке первого появления: порядок обхода соответствия не гарантирован, и колонки шли бы вразнобой.ЗначенияКолонок приводятся к верхнему регистру. Имена колонок регистр не различают, а ключи соответствия различают: «Price» и «price» из разных объектов JSON без ВРег дали бы два ключа, и второй Колонки.Добавить упал бы на дубле имени.ОписаниеТиповПоЗначениям пропускает Неопределено и Null. Строки и числа в одной колонке дадут составной тип — допустимо, но в условиях запроса неудобно.НоваяСтрока[КлючИЗначение.Ключ], а не через ЗаполнитьЗначенияСвойств, поэтому функция принимает и соответствия, которые получаются после чтения JSON.Таблицу из одной строки даёт СтруктураВТаблицуЗначений из листинга выше: она кладёт структуру в массив и вызывает общую функцию. Это нужно, когда следующий шаг принимает только таблицу — запрос с параметром-таблицей или загрузка в табличную часть.
Иногда нужно другое: каждый ключ — отдельная строка, как в окне просмотра значений отладчика. Тогда колонок две:
// Каждый ключ структуры - отдельная строка таблицы
Функция СтруктураВТаблицуКлючЗначение(Структура) Экспорт
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Ключ", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)));
Таблица.Колонки.Добавить("Значение");
Для Каждого КлючИЗначение Из Структура Цикл
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Ключ = КлючИЗначение.Ключ;
НоваяСтрока.Значение = КлючИЗначение.Значение;
КонецЦикла;
Возврат Таблица;
КонецФункции
Колонка «Значение» здесь без типа, потому что значения у ключей разные. Для вывода на экран это нормально, для запроса — нет.
Готовые функции для коллекций лежат в общих модулях ОбщегоНазначения (сервер) и ОбщегоНазначенияКлиентСервер. Для перевода массива в таблицу значений через БСП и обратно пригодятся четыре:
| Функция БСП | Что делает |
|---|---|
ОбщегоНазначенияКлиентСервер.ДополнитьТаблицуИзМассива(Таблица, Массив, ИмяПоля) | Добавляет по строке на каждый элемент массива и записывает значение в колонку ИмяПоля. Колонку создают заранее |
ОбщегоНазначенияКлиентСервер.ДополнитьТаблицу(ТаблицаИсточник, ТаблицаПриемник) | Добавляет в приёмник по строке на каждую строку источника и заполняет её по именам колонок |
ОбщегоНазначения.ТаблицаЗначенийВМассив(ТаблицаЗначений) | Обратное преобразование: таблица в массив структур |
ОбщегоНазначения.СтрокаТаблицыЗначенийВСтруктуру(СтрокаТаблицыЗначений) | Одна строка таблицы в структуру |
Массив простых значений загружает ДополнитьТаблицуИзМассива — без своего цикла и без ЗагрузитьКолонку. Для массива структур в конфигурации на БСП часто берут ДополнитьТаблицу. Массив в описании её параметров не указан, но реализация перебирает источник циклом Для Каждого и заполняет строки через ЗаполнитьЗначенияСвойств, поэтому массив структур она на практике принимает. Это опора на реализацию, а не на контракт.
// Серверный код, конфигурация на БСП.
// Ссылки - массив ссылок на номенклатуру (например, выделенные строки списка).
Функция ТаблицаНоменклатуры(Ссылки) Экспорт
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
// По строке на каждый элемент массива, значение - в колонку Номенклатура
ОбщегоНазначенияКлиентСервер.ДополнитьТаблицуИзМассива(Таблица, Ссылки, "Номенклатура");
Возврат Таблица;
КонецФункции
// Товары - массив структур с ключами Артикул и Цена.
Функция ТаблицаЦен(Товары) Экспорт
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
Таблица.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
// По строке на каждую структуру, заполнение по именам колонок
ОбщегоНазначенияКлиентСервер.ДополнитьТаблицу(Товары, Таблица);
Возврат Таблица;
КонецФункции
Сверьте обе процедуры со своей версией БСП. Колонки по ключам ни одна из них не создаёт: для данных неизвестного состава нужна своя функция вроде МассивСтруктурВТаблицуЗначений.
Главная причина собирать таблицу — запрос. Параметром можно передать и сам массив, но он годится только для условия В (&Массив). Если нужно соединение — например, найти артикулы, которых нет в справочнике, — нужна таблица: её помещают во временную таблицу и дальше работают как с обычной.
// Возвращает артикулы, для которых в справочнике нет номенклатуры.
// Артикулы - массив строк.
Функция НеНайденныеАртикулы(Артикулы) Экспорт
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
Для Счетчик = 1 По Артикулы.Количество() Цикл
Таблица.Добавить();
КонецЦикла;
Таблица.ЗагрузитьКолонку(Артикулы, "Артикул");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Артикул КАК Артикул
|ПОМЕСТИТЬ ВТ_Артикулы
|ИЗ
| &Артикулы КАК Т
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗЛИЧНЫЕ
| ВТ_Артикулы.Артикул КАК Артикул
|ИЗ
| ВТ_Артикулы КАК ВТ_Артикулы
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ВТ_Артикулы.Артикул = Номенклатура.Артикул
|ГДЕ
| Номенклатура.Ссылка ЕСТЬ NULL";
Запрос.УстановитьПараметр("Артикулы", Таблица);
Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Артикул");
КонецФункции
РАЗЛИЧНЫЕ во втором запросе убирает повторы, если один артикул пришёл во входном массиве дважды. Если реквизит номенклатуры называется иначе, замените его в тексте запроса. Требования к таблице-параметру: у колонок задан тип, который можно хранить в базе (строка, число, дата, булево, ссылка), у строк, по которым соединяют или группируют, указана длина. Вложенные структуры и массивы в запрос не передаются.
ДополнитьТаблицуИзМассива.Новый ТаблицаЗначений падает с ошибкой «Тип не определен (ТаблицаЗначений)». Перенесите код на сервер. Вернуть таблицу на клиент тоже нельзя: передавайте массив структур или реквизит формы.Колонки.Добавить("Артикул") без описания типов создаёт колонку произвольного типа. В коде она работает, а запрос с такой таблицей-параметром завершается ошибкой «Тип не может быть выбран в запросе».Новый ОписаниеТипов("Строка") — строка неограниченной длины. Соединение по ней даёт «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов». Задайте КвалификаторыСтроки.ЗаполнитьЗначенияСвойств молча пропускает ключи без пары: «Колличество» вместо «Количество» — и колонка пустая. А ключи из JSON вроде "price-rub" или "1c_code" не годятся в имя колонки, их переименовывают до загрузки.Да, если нужен только отбор: ГДЕ Номенклатура.Ссылка В (&Список) и Запрос.УстановитьПараметр("Список", Массив). Таблица нужна, когда из переданных значений надо получить строки: соединить их с другими таблицами, найти отсутствующие, передать вместе с ними количество или цену.
Одна колонка выгружается в массив методом ВыгрузитьКолонку("ИмяКолонки"). Если нужны все колонки, в БСП есть ОбщегоНазначения.ТаблицаЗначенийВМассив: каждая строка становится структурой. Такой массив, в отличие от самой таблицы, можно вернуть на клиент.
Добавьте реквизит формы типа «ТаблицаЗначений» с теми же колонками и на сервере вызовите ЗначениеВРеквизитФормы(Таблица, "ИмяРеквизита"). Программно колонки реквизита создаёт метод формы ИзменитьРеквизиты.
Промежуточная таблица не нужна: в цикле вызывайте ЗаполнитьЗначенияСвойств(Объект.Товары.Добавить(), Структура). Если таблица значений уже собрана, Объект.Товары.Загрузить(Таблица) на сервере заменит все строки табличной части, колонки сопоставятся по именам.
Да. ЗагрузитьКолонку, ЗаполнитьЗначенияСвойств и ОписаниеТипов есть в платформе задолго до 8.3, а в обычном приложении таблица значений доступна и в толстом клиенте. Ограничение одно — СтрРазделить: при режиме совместимости ниже 8.3.6 разбейте строку функцией БСП СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок или своим циклом.
Перевод массива в таблицу обычно лишь шаг большой задачи: загрузить прайс поставщика, сопоставить артикулы с номенклатурой, собрать из этого отчёт или обработку. Если такую задачу проще отдать, посмотрите услугу доработки отчётов и обработок 1С или сразу разместите задачу: оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.