Массив в таблицу значений 1С и обратно: структуры, табличные части и Excel
Данные в 1С постоянно перекладываются из одной коллекции в другую: массив превращают в таблицу значений, таблицу — в массив структур для передачи на клиент, табличную часть выгружают для свёртки, а Excel читают в табличный документ. В статье — рабочий код для каждого направления, функции БСП, которые делают то же одной строкой, и ошибки, на которых чаще всего спотыкаются.
Задача: переложить данные из одной коллекции в другую
В 1С данные постоянно меняют «упаковку»: результат запроса выгружают в таблицу значений, таблицу значений превращают в массив структур, чтобы передать на клиент, табличную часть документа выгружают для свёртки, а файл Excel читают в табличный документ и уже из него собирают таблицу. Универсального метода «преобразовать что угодно во что угодно» в платформе нет, но для каждого направления есть короткий и проверенный приём. Основы работы с самой таблицей значений — создание, поиск, сортировка — разобраны в статье про таблицу значений, здесь речь только о преобразованиях.
| Из чего | Во что | Чем проще всего |
|---|---|---|
| Массив значений | Колонка таблицы значений | ЗагрузитьКолонку() |
| Колонка таблицы значений | Массив | ВыгрузитьКолонку() |
| Массив структур | Таблица значений | Цикл + ЗаполнитьЗначенияСвойств() |
| Таблица значений | Массив структур | Цикл или ОбщегоНазначения.ТаблицаЗначенийВМассив() (БСП) |
| Строка таблицы значений | Структура | ОбщегоНазначения.СтрокаТаблицыЗначенийВСтруктуру() (БСП) |
| Табличная часть | Таблица значений | Выгрузить() |
| Табличный документ, Excel | Таблица значений | Прочитать() + обход ячеек |
Массив в таблицу значений
Если массив содержит простые значения (ссылки, строки, числа), его удобно положить в одну колонку. Метод ЗагрузитьКолонку заполняет колонку значениями массива по порядку и сам добавляет недостающие строки.
Товары = Новый Массив;
Товары.Добавить(Справочники.Номенклатура.НайтиПоКоду("00001"));
Товары.Добавить(Справочники.Номенклатура.НайтиПоКоду("00002"));
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Таблица.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
Таблица.ЗагрузитьКолонку(Товары, "Номенклатура");
// В таблице две строки, колонка "Количество" заполнена нулями
Обратная операция — Таблица.ВыгрузитьКолонку("Номенклатура") — возвращает массив значений колонки в порядке строк. Её часто используют, чтобы передать список ссылок параметром в запрос с условием В (&Список).
В конфигурациях на БСП для того же есть ОбщегоНазначенияКлиентСервер.ДополнитьТаблицуИзМассива(Таблица, Массив, ИмяПоля): она не перезаписывает существующие строки, а добавляет новые, по одной на каждый элемент массива. Это удобно, когда таблица уже частично заполнена.
Массив структур в таблицу значений
Массив структур получается после разбора JSON, из ответа веб-сервиса или когда данные пришли с клиента. Чтобы превратить его в таблицу, нужно один раз создать колонки — по ключам первой структуры — и затем перенести каждую структуру в новую строку. Перенос по совпадающим именам делает ЗаполнитьЗначенияСвойств, поэтому код не зависит от количества полей.
Функция МассивСтруктурВТаблицуЗначений(МассивСтруктур) Экспорт
Таблица = Новый ТаблицаЗначений;
Если МассивСтруктур.Количество() = 0 Тогда
Возврат Таблица;
КонецЕсли;
Для Каждого КлючИЗначение Из МассивСтруктур[0] Цикл
Таблица.Колонки.Добавить(КлючИЗначение.Ключ);
КонецЦикла;
Для Каждого Элемент Из МассивСтруктур Цикл
ЗаполнитьЗначенияСвойств(Таблица.Добавить(), Элемент);
КонецЦикла;
Возврат Таблица;
КонецФункции
Колонки здесь создаются без типа. Для обработки в памяти этого достаточно, но если таблицу потом передают в запрос, нетипизированная колонка вызовет ошибку «Тип не может быть выбран в запросе». В таком случае задавайте тип явно: либо передайте в функцию заранее созданную таблицу-шаблон, либо добавляйте колонки с Новый ОписаниеТипов(...). Если структуры в массиве разные, соберите полный набор ключей по всем элементам, иначе поля, которых нет в первой структуре, потеряются.
Таблица значений в массив структур
Таблица значений не передаётся с сервера на клиент, а массив структур с примитивными значениями и ссылками — передаётся. Отсюда самая частая причина преобразования: подготовить данные на сервере и вернуть их в клиентский обработчик.
&НаСервереБезКонтекста
Функция ДанныеДляКлиента(Документ)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| Товары.Количество КАК Количество
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Документ);
Таблица = Запрос.Выполнить().Выгрузить();
Результат = Новый Массив;
ИменаКолонок = "";
Для Каждого Колонка Из Таблица.Колонки Цикл
ИменаКолонок = ИменаКолонок + ?(ИменаКолонок = "", "", ",") + Колонка.Имя;
КонецЦикла;
Для Каждого Строка Из Таблица Цикл
Элемент = Новый Структура(ИменаКолонок);
ЗаполнитьЗначенияСвойств(Элемент, Строка);
Результат.Добавить(Элемент);
КонецЦикла;
Возврат Результат;
КонецФункции
Имена документа и табличной части в примере условные — подставьте свои. В БСП тот же результат даёт одна строка: ОбщегоНазначения.ТаблицаЗначенийВМассив(Таблица). Функция серверная и, как указано в её описании в документации БСП, подходит для передачи на клиент, только если в таблице нет значений, которые клиент принять не может (например, объектов или вложенных таблиц значений).
Таблицу значений в структуру
Под запросом «таблица значений в структуру» обычно понимают одно из трёх действий, и решаются они по-разному.
Одна строка — в структуру
Нужна, когда строку таблицы надо передать в другую процедуру или на клиент. В БСП есть готовая ОбщегоНазначения.СтрокаТаблицыЗначенийВСтруктуру(СтрокаТаблицы). Без БСП — тот же приём, что выше: структура с именами колонок и ЗаполнитьЗначенияСвойств.
Вся таблица — в структуру колонок
Каждый ключ структуры — имя колонки, значение — массив значений этой колонки. Такой вид удобен для сериализации в JSON (см. статью о работе с JSON) и для построения графиков.
СтруктураКолонок = Новый Структура;
Для Каждого Колонка Из Таблица.Колонки Цикл
СтруктураКолонок.Вставить(Колонка.Имя, Таблица.ВыгрузитьКолонку(Колонка.Имя));
КонецЦикла;
Таблица — в соответствие по ключу
Если по таблице многократно ищут строку по одному полю, быстрее один раз построить соответствие «ключ → строка», чем каждый раз вызывать Найти. Ключ должен быть уникальным: при повторе последняя строка перезапишет предыдущую.
ПоНоменклатуре = Новый Соответствие;
Для Каждого Строка Из Таблица Цикл
ПоНоменклатуре.Вставить(Строка.Номенклатура, Строка);
КонецЦикла;
НайденнаяСтрока = ПоНоменклатуре.Получить(Товар); // Неопределено, если нет
Выгрузить табличную часть в таблицу значений
У табличной части объекта есть метод Выгрузить(): он возвращает таблицу значений с теми же колонками и типами, включая НомерСтроки. Вторым параметром можно перечислить только нужные колонки — таблица получится легче.
ДокументОбъект = Документ.ПолучитьОбъект();
Товары = ДокументОбъект.Товары.Выгрузить(, "Номенклатура, Количество, Сумма");
Товары.Свернуть("Номенклатура", "Количество, Сумма");
// Вернуть обратно в табличную часть
ДокументОбъект.Товары.Загрузить(Товары);
Метод Загрузить заменяет всё содержимое табличной части, колонки сопоставляются по именам, а отсутствующие колонки остаются пустыми. Поэтому если вы выгружали не все колонки, после загрузки остальные реквизиты строк будут очищены.
В управляемой форме реквизит Объект — это данные формы, а не объект. На сервере у его табличной части тоже есть Выгрузить(), а если нужна полноценная табличная часть объекта со всеми методами, преобразуйте реквизит:
&НаСервере
Процедура СвернутьТовары()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
Таблица = ДокументОбъект.Товары.Выгрузить();
Таблица.Свернуть("Номенклатура", "Количество, Сумма");
ДокументОбъект.Товары.Загрузить(Таблица);
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Табличный документ в таблицу значений
Табличный документ — это сетка ячеек без типов и колонок, поэтому преобразование всегда сводится к обходу ячеек. Размер заполненной части дают свойства ВысотаТаблицы и ШиринаТаблицы, а текст ячейки — Область(Строка, Колонка).Текст. Функция ниже берёт заголовки из первой строки, а колонкам таблицы даёт имена К1, К2 и так далее: заголовки из файлов часто содержат пробелы и знаки, недопустимые в имени колонки, поэтому исходный текст сохраняется в заголовке колонки.
Функция ТабличныйДокументВТаблицуЗначений(ТабДок, ПерваяСтрокаЗаголовок = Истина) Экспорт
Таблица = Новый ТаблицаЗначений;
ВсегоСтрок = ТабДок.ВысотаТаблицы;
ВсегоКолонок = ТабДок.ШиринаТаблицы;
Для НомерКолонки = 1 По ВсегоКолонок Цикл
Заголовок = ?(ПерваяСтрокаЗаголовок,
СокрЛП(ТабДок.Область(1, НомерКолонки).Текст), "");
Таблица.Колонки.Добавить("К" + Формат(НомерКолонки, "ЧГ=0"),
Новый ОписаниеТипов("Строка"), Заголовок);
КонецЦикла;
ПерваяСтрокаДанных = ?(ПерваяСтрокаЗаголовок, 2, 1);
Для НомерСтроки = ПерваяСтрокаДанных По ВсегоСтрок Цикл
НоваяСтрока = Таблица.Добавить();
ЕстьДанные = Ложь;
Для НомерКолонки = 1 По ВсегоКолонок Цикл
Текст = СокрЛП(ТабДок.Область(НомерСтроки, НомерКолонки).Текст);
НоваяСтрока[НомерКолонки - 1] = Текст;
ЕстьДанные = ЕстьДанные Или Не ПустаяСтрока(Текст);
КонецЦикла;
Если Не ЕстьДанные Тогда
Таблица.Удалить(НоваяСтрока); // пропускаем пустые строки
КонецЕсли;
КонецЦикла;
Возврат Таблица;
КонецФункции
Все значения здесь строковые. Числа и даты преобразуйте при переносе в документ или справочник: Число(Текст) и Дата(...) вызывают исключение на некорректных данных, поэтому оборачивайте их в Попытка и собирайте ошибки по строкам, чтобы показать пользователю номер проблемной строки файла.
Excel в таблицу значений
Начиная с версии 8.3.6 табличный документ умеет читать файлы XLS, XLSX и ODS без установленного Excel — это зафиксировано в описании изменений версии 8.3.6. Тогда же у метода Прочитать() появился параметр способа чтения значений (системное перечисление СпособЧтенияЗначенийТабличногоДокумента). Значит, загрузка Excel в таблицу значений — это два шага: прочитать файл в табличный документ и обойти его функцией из предыдущего раздела.
Чтение выполняется на сервере, а файл выбирает пользователь на клиенте, поэтому сначала файл помещают во временное хранилище (любым из методов помещения файла, доступных в вашей версии платформы), а на сервер передают только адрес.
&НаСервере
Процедура ЗагрузитьИзExcelНаСервере(АдресФайла, Расширение)
ИмяФайла = ПолучитьИмяВременногоФайла(Расширение);
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресФайла);
ДвоичныеДанные.Записать(ИмяФайла);
ТабДок = Новый ТабличныйДокумент;
Попытка
ТабДок.Прочитать(ИмяФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
УдалитьФайлы(ИмяФайла);
ВызватьИсключение "Не удалось прочитать файл: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
КонецПопытки;
УдалитьФайлы(ИмяФайла);
Таблица = ТабличныйДокументВТаблицуЗначений(ТабДок, Истина);
// Дальше: проверка, приведение типов, перенос в табличную часть
Сообщить("Прочитано строк: " + Таблица.Количество());
КонецПроцедуры
Расширение временного файла должно совпадать с исходным ("xlsx", "xls"), иначе платформа может не определить формат. Читается первый лист книги; если данные на другом листе, попросите пользователя сохранить нужный лист отдельно или вынести его первым. Объединённые ячейки, формулы и скрытые строки тоже лучше обсудить с пользователем заранее: надёжнее всего грузить «плоскую» таблицу с одной строкой заголовков.
В типовых конфигурациях для загрузки в табличные части и справочники есть подсистема БСП «Загрузка данных из файла» со своим помощником сопоставления колонок. Если задача стандартная, сначала проверьте, не решает ли её эта подсистема — общий обзор возможностей библиотеки есть в статье про БСП.
Помощники БСП для коллекций
| Функция | Что делает |
|---|---|
ОбщегоНазначения.ТаблицаЗначенийВМассив(Таблица) | Таблица значений → массив структур |
ОбщегоНазначения.СтрокаТаблицыЗначенийВСтруктуру(Строка) | Строка таблицы → структура |
ОбщегоНазначенияКлиентСервер.ДополнитьТаблицуИзМассива(Таблица, Массив, ИмяПоля) | Добавляет по строке на каждый элемент массива |
ОбщегоНазначенияКлиентСервер.ДополнитьТаблицу(Источник, Приемник) | Дописывает строки одной таблицы в другую |
ОбщегоНазначения.ВыгрузитьКолонку(Коллекция, ИмяКолонки, ТолькоУникальные) | Колонка любой обходимой коллекции → массив, при необходимости без повторов |
Состав и расположение функций меняются от версии к версии библиотеки. Прежде чем вызывать функцию, найдите её глобальным поиском в своей конфигурации и прочитайте комментарий к ней — там описаны параметры и ограничения.
Типичные ошибки
- «Тип не может быть выбран в запросе» — в запрос передана таблица с нетипизированными колонками. Добавляйте колонки с
ОписаниеТипов. - Попытка вернуть таблицу значений на клиент из серверной функции. Преобразуйте её в массив структур или поместите во временное хранилище.
- Потерянные поля при переносе массива структур: колонки созданы по первому элементу, а у других элементов есть дополнительные ключи.
- Очищенные реквизиты табличной части после
Загрузить()таблицы, в которую выгружали не все колонки. - Недопустимое имя колонки при создании колонок из заголовков Excel: пробелы, точки и цифры в начале имени приводят к ошибке.
- Числа в виде текста из табличного документа: неразрывные пробелы и запятые в качестве разделителя мешают
Число(). Очищайте строку перед преобразованием.
Актуальность для новых версий
Методы ЗагрузитьКолонку, ВыгрузитьКолонку, Выгрузить и Загрузить работают одинаково во всех версиях 8.3. Чтение файлов XLS, XLSX и ODS в табличный документ без установленного Excel доступно начиная с версии 8.3.6. Набор функций БСП и общие модули, в которых они находятся, зависят от версии библиотеки, поэтому сверяйтесь с комментариями к функциям в своей конфигурации.
Частые вопросы
Как быстро загрузить массив в колонку таблицы значений?
Создайте колонку и вызовите Таблица.ЗагрузитьКолонку(Массив, "ИмяКолонки"). Метод заполнит колонку значениями массива по порядку и при необходимости сам добавит строки.
Как преобразовать массив структур в таблицу значений?
Создайте колонки по ключам структур, затем в цикле добавляйте строку и переносите в неё структуру через ЗаполнитьЗначенияСвойств. Если таблица пойдёт в запрос, задайте колонкам типы.
Можно ли передать таблицу значений на клиент?
Нет, таблица значений на клиенте недоступна. Преобразуйте её в массив структур, например функцией ОбщегоНазначения.ТаблицаЗначенийВМассив из БСП, или используйте реквизит формы типа ТаблицаЗначений.
Как выгрузить табличную часть документа в таблицу значений?
Вызовите у табличной части объекта метод Выгрузить(). Вторым параметром можно перечислить нужные колонки. Обратно данные загружаются методом Загрузить(), который полностью заменяет содержимое табличной части.
Нужен ли установленный Excel, чтобы прочитать файл xlsx в 1С?
Нет. Начиная с версии 8.3.6 табличный документ читает XLS, XLSX и ODS методом Прочитать() без Excel. После чтения обойдите ячейки и перенесите данные в таблицу значений.
Почему при передаче таблицы в запрос возникает ошибка «Тип не может быть выбран в запросе»?
У колонок таблицы не задан тип значения. Создавайте колонки с описанием типов, например Новый ОписаниеТипов("СправочникСсылка.Номенклатура"), тогда запрос сможет выбрать данные из таблицы.