Колонки таблицы значений: создаём и заполняем кодом
Содержание
Колонку в таблицу значений добавляют методом ТаблицаЗначений.Колонки.Добавить(). В параметрах указывают имя колонки, тип значения, заголовок для формы и ширину — три последних необязательны. Сам объект создаётся через Новый ТаблицаЗначений, после чего к коллекции Колонки можно обращаться на добавление, вставку и удаление в любой момент.
Ниже — рабочий синтаксис для 1С 8.3 (платформа 8.3.2x, встроенный язык): как задать колонке конкретный тип через ОписаниеТипов, как собрать набор колонок из структуры, скопировать его из другой таблицы, объявить составной тип и чем это оборачивается при заполнении. Все примеры кода запускаются как есть — в модуле формы, общем модуле или обработке.
Создание таблицы и добавление первой колонки
Таблица значений — это объект в памяти, он не хранится в базе и живёт только во время работы кода. Создаём пустую таблицу и сразу добавляем колонку:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура");
ТЗ.Колонки.Добавить("Количество");
ТЗ.Колонки.Добавить("Сумма");
Метод Добавить() возвращает саму добавленную колонку — её удобно сохранить в переменную, если дальше нужно донастроить свойства:
Колонка = ТЗ.Колонки.Добавить("Цена");
Колонка.Заголовок = "Цена за единицу";
Колонка.Ширина = 12;
Полная сигнатура: Добавить(<Имя>, <Тип>, <Заголовок>, <Ширина>). Обязателен только первый параметр, да и тот можно опустить — тогда платформа присвоит имя вида Колонка1. На практике имя задают всегда: по нему идёт обращение к данным строки.
Имя колонки подчиняется правилам идентификатора: только буквы, цифры и подчёркивание, первый символ — не цифра. Пробелы и знаки %, -, № недопустимы. Если нужен «человеческий» текст с пробелами — это свойство Заголовок, оно отображается в шапке таблицы на форме.
Чтобы поставить колонку не в конец, а на нужную позицию, используют Вставить() с индексом (нумерация с нуля):
// вставить колонку "Артикул" перед первой колонкой
ТЗ.Колонки.Вставить(0, "Артикул");
Проверить, что колонка уже есть, помогает Найти() — он вернёт Неопределено, если колонки нет:
Если ТЗ.Колонки.Найти("Скидка") = Неопределено Тогда
ТЗ.Колонки.Добавить("Скидка");
КонецЕсли;
Тип колонки и его квалификаторы
Если второй параметр не задать, колонка принимает значения любого типа — это удобно для набросков, но опасно в расчётах: туда случайно ляжет строка вместо числа, и ошибку вы увидите далеко от места, где она возникла. Поэтому тип задают явно через объект ОписаниеТипов.
Самый короткий вариант — передать имя типа строкой:
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТЗ.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("ДатаОтгрузки", Новый ОписаниеТипов("Дата"));
Для чисел, строк и дат к типу добавляют квалификаторы — они ограничивают разрядность, длину и состав. Это пригодится, когда таблица выгружается в табличный документ или участвует в арифметике:
// число: 15 знаков, 2 в дробной части, любой знак
КвЧисла = Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Любой);
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", КвЧисла));
// строка: не длиннее 100 символов, фиксированной длины
КвСтроки = Новый КвалификаторыСтроки(100, ДопустимаяДлина.Переменная);
ТЗ.Колонки.Добавить("Комментарий", Новый ОписаниеТипов("Строка", , КвСтроки));
// дата: только дата, без времени
КвДаты = Новый КвалификаторыДаты(ЧастиДаты.Дата);
ТЗ.Колонки.Добавить("ДатаДок", Новый ОписаниеТипов("Дата", , , КвДаты));
Обратите внимание на позицию параметров: в ОписаниеТипов квалификаторы чисел идут вторым аргументом, строки — третьим, даты — четвёртым, поэтому для строки и даты перед квалификатором стоят пропущенные запятые.
Колонка может хранить и ссылку на объект базы — тогда тип указывают полным именем:
ТЗ.Колонки.Добавить("Товар", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ.Колонки.Добавить("Документ", Новый ОписаниеТипов("ДокументСсылка.РеализацияТоваровУслуг"));
Добавление набора колонок по описанию структуры
Когда колонок много и их список известен заранее, перечислять каждую отдельной строкой — лишний код. Храните описание в коллекции и добавляйте колонки циклом. Удобно использовать Соответствие, где ключ — имя колонки, значение — её тип:
ОписаниеКолонок = Новый Соответствие;
ОписаниеКолонок.Вставить("Код", Новый ОписаниеТипов("Строка"));
ОписаниеКолонок.Вставить("Количество", Новый ОписаниеТипов("Число"));
ОписаниеКолонок.Вставить("Товар", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ = Новый ТаблицаЗначений;
Для Каждого Элемент Из ОписаниеКолонок Цикл
ТЗ.Колонки.Добавить(Элемент.Ключ, Элемент.Значение);
КонецЦикла;
Такой подход хорош, когда состав колонок зависит от настроек или приходит извне. Например, набор можно собрать из колонок результата запроса — у выборки есть метод, перечисляющий имена полей, и по ним колонки создаются автоматически. Главное правило одно: имена не должны повторяться, иначе на втором Добавить() с тем же именем платформа выдаст ошибку.
Если список имён приходит из внешнего источника (файл, обмен, пользовательская настройка), проверяйте каждое имя через Найти() перед добавлением и очищайте от недопустимых символов. Иначе одно «кривое» имя оборвёт заполнение всей таблицы.
Копирование колонок из другой таблицы
Если нужна таблица с той же структурой, что у уже существующей, колонки не перебирают вручную. Для этого есть готовый метод СкопироватьКолонки() — он возвращает новую таблицу с теми же колонками, но без строк:
ПустаяКопия = ИсходнаяТЗ.СкопироватьКолонки();
Второй способ — метод Скопировать(). Если передать первым параметром пустой массив строк, вернётся копия структуры без данных:
ТолькоСтруктура = ИсходнаяТЗ.Скопировать(Новый Массив);
А чтобы скопировать не все колонки, а только часть, перечислите их имена через запятую вторым параметром:
// копия с данными, но лишь по двум колонкам
ЧастьТаблицы = ИсходнаяТЗ.Скопировать( , "Номенклатура,Сумма");
// только структура этих двух колонок, без строк
СтруктураЧасти = ИсходнаяТЗ.Скопировать(Новый Массив, "Номенклатура,Сумма");
Разница между методами такая: СкопироватьКолонки() всегда отдаёт пустую таблицу и читается нагляднее, а Скопировать() гибче — он же умеет переносить и сами строки, в том числе по отбору. Для чистого клонирования структуры берите первый, для копии с данными — второй.
Колонки с составным типом
Одна колонка может хранить значения нескольких типов — это составной тип. Типы перечисляют в строке через запятую:
ТЗ.Колонки.Добавить("Объект",
Новый ОписаниеТипов("СправочникСсылка.Номенклатура, СправочникСсылка.Контрагенты"));
ТЗ.Колонки.Добавить("Значение",
Новый ОписаниеТипов("Строка, Число, Дата, Булево"));
Такая колонка пригодится, когда в одном поле лежат разнородные данные: например, таблица настроек, где значение бывает и числом, и строкой, и флагом.
При чтении из составной колонки проверяйте фактический тип функцией ТипЗнч(), прежде чем применять его как число или дату:
Для Каждого Строка Из ТЗ Цикл
Если ТипЗнч(Строка.Значение) = Тип("Число") Тогда
Итог = Итог + Строка.Значение;
КонецЕсли;
КонецЦикла;
Если тип колонки не задан вовсе, она тоже хранит любые значения — но это не то же самое, что составной тип. У составного типа список разрешённых типов ограничен и контролируется платформой: попытка записать значение неразрешённого типа вызовет ошибку приведения, и это как раз защищает данные от мусора.
Заполнение и чтение данных новой колонки
Колонка без строк бесполезна, поэтому после настройки структуры добавляют строки методом Добавить() самой таблицы (не коллекции колонок) и присваивают значения по именам колонок:
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Товар", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ТЗ.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Товар = "Бумага А4";
НоваяСтрока.Количество = 5;
НоваяСтрока.Цена = 320.50;
К значению ячейки обращаются тремя способами: по имени через точку (Строка.Цена), по имени строкой (Строка["Цена"]) и по индексу колонки (Строка[2]). Строковая форма нужна, когда имя колонки заранее неизвестно и лежит в переменной; обращение по индексу — когда таблица обрабатывается обобщённо, без привязки к именам.
Заполнить новую колонку целиком из готового массива быстрее, чем перебирать строки, — для этого есть ЗагрузитьКолонку(). Массив должен содержать ровно столько элементов, сколько строк в таблице, и порядок совпадёт построчно:
МассивСкидок = Новый Массив;
МассивСкидок.Добавить(0);
МассивСкидок.Добавить(50); // по числу строк таблицы
ТЗ.Колонки.Добавить("Скидка", Новый ОписаниеТипов("Число"));
ТЗ.ЗагрузитьКолонку(МассивСкидок, "Скидка");
Чтобы заполнить новую колонку расчётом по уже имеющимся, пройдите таблицу циклом:
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
Для Каждого Строка Из ТЗ Цикл
Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;
Для нашей строки 5 * 320.50 = 1602.50 — это и ляжет в колонку Сумма. Выгрузить всю колонку в массив помогает ВыгрузитьКолонку(), а быстро просуммировать числовую колонку — Итог():
МассивЦен = ТЗ.ВыгрузитьКолонку("Цена"); // массив значений колонки
ОбщаяСумма = ТЗ.Итог("Сумма"); // 1602.50 для одной строки
Удаляют колонку методом Колонки.Удалить() — ему передают имя, индекс или саму колонку:
ТЗ.Колонки.Удалить("Цена");
Изменение типа и свойств готовой колонки
Колонку не обязательно пересоздавать, чтобы поменять её настройки, — у объекта колонки есть доступные на запись свойства. Найдите колонку и присвойте новое значение:
Колонка = ТЗ.Колонки.Найти("Сумма");
Колонка.Имя = "СуммаСНДС"; // переименование
Колонка.Заголовок = "Сумма с НДС"; // подпись в шапке
Колонка.Ширина = 15; // ширина на форме
Колонка.ТипЗначения = Новый ОписаниеТипов("Число",
Новый КвалификаторыЧисла(18, 2)); // новый тип
Со сменой ТипЗначения есть нюанс. Если колонка уже заполнена, значения, не подходящие под новый тип, при следующей записи придётся приводить вручную. Новый тип не перепроверяет задним числом то, что уже лежит в ячейках, — он действует на присваивания после смены. Поэтому расширять тип (например, добавить к числу строку) безопасно, а сужать — только на заведомо совместимых данных.
Расширить набор типов, не потеряв текущий, помогает конструктор ОписаниеТипов с исходным описанием первым параметром — он принимает уже существующее ОписаниеТипов и массив добавляемых типов. Это удобно, когда к колонке по ходу логики добавляется ещё один допустимый тип значения.
Частые ошибки при работе с колонками
Ошибки с колонками почти всегда ловятся не там, где написаны, — поэтому разберём симптомы и причины отдельно.
- 🔴 «Поле объекта не обнаружено (ИмяКолонки)» — обращение к колонке, которой нет: опечатка в имени или колонку забыли добавить. Проверьте имя через
ТЗ.Колонки.Найти(). - 🔴 «Преобразование значения к типу Число (Дата…) не может быть выполнено» — в колонку с заданным типом пишут значение другого типа. Приведите значение (
Число(),Строка()) или ослабьте тип колонки. - 🔴 Ошибка при повторном
Добавить()— колонка с таким именем уже есть. Имена колонок внутри одной таблицы уникальны. - 🟡 Пустые ячейки вместо расчёта — тип колонки не задан, и присвоенное значение «проскочило» без контроля, либо имя колонки в коде не совпадает с объявленным (например, регистр или лишний пробел в
Заголовке, принятом за имя). - 🟡 Перепутаны
ЗаголовокиИмя— в шапке таблицы на форме вы видите имяКолонка1вместо подписи. Заголовок задаётся отдельным параметром или свойствомЗаголовок.
Если вы удаляете колонки внутри цикла по ТЗ.Колонки, идите по коллекции с конца к началу — иначе индексы сдвинутся, и часть колонок пропустится:
Для Сч = ТЗ.Колонки.Количество() - 1 По 0 Цикл
ТЗ.Колонки.Удалить(Сч);
КонецЦикла;
Отдельная тонкость — регистр имён. В 1С имена колонок регистронезависимы при обращении через точку (Строка.сумма и Строка.Сумма равнозначны), но при строковом обращении Строка["Сумма"] лучше писать имя точно так, как объявили, чтобы не путаться самому.
Частые вопросы
Можно ли добавить колонку в таблицу, в которой уже есть строки?
Колонки.Добавить() работает в любой момент. В существующих строках новая колонка получит пустое значение (для числа — 0, для строки — пустая строка), а дальше её заполняют циклом по таблице.Чем `ТаблицаЗначений` отличается от `ДеревоЗначений`?
Строки у каждой строки). Колонки у дерева добавляются тем же методом Колонки.Добавить().Как переименовать колонку, не теряя данные?
Имя нужной колонки: ТЗ.Колонки.Найти("СтароеИмя").Имя = "НовоеИмя". Данные в ячейках сохранятся, менять их не нужно.Почему в колонку без типа можно записать что угодно, а это плохо?
ОписаниеТипов хранит значения любого типа и не контролирует их. Ошибочное значение не вызовет ошибку сразу — она всплывёт позже, при расчёте или выгрузке, и найти причину будет труднее. Поэтому тип задают явно почти всегда.Как скопировать только структуру таблицы без строк?
СкопироватьКолонки() возвращает пустую таблицу с теми же колонками. Альтернатива — Скопировать(Новый Массив): пустой массив строк в первом параметре даёт копию структуры без данных.