Заполнение колонок Таблицы значений: код и методы

· 8 мин

Содержание

Чтобы записать значение в колонку Таблицы значений, обращаются к ячейке через строку и имя колонки: Строка.Количество = 5. Для одной строки хватит прямого присваивания, для всей таблицы — цикл Для Каждого или встроенные методы ЗаполнитьЗначения и ЗагрузитьКолонку.

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

Создаём таблицу и описываем колонки

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

ТЗ = Новый ТаблицаЗначений;

Пустая таблица колонок не имеет. Их добавляют методом Колонки.Добавить. Первый параметр — имя колонки, по которому вы потом обращаетесь к ячейке:

ТЗ.Колонки.Добавить("Наименование");
ТЗ.Колонки.Добавить("Количество");
ТЗ.Колонки.Добавить("Цена");

Такие колонки — без типа. В них можно положить что угодно: число, строку, ссылку. Это гибко, но опасно: опечатка в расчёте попадёт в ячейку молча. Поэтому колонкам лучше задавать тип через ОписаниеТипов — вторым параметром:

ТЗ.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Количество",  Новый ОписаниеТипов("Число"));
ТЗ.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число",
    Новый КвалификаторыЧисла(15, 2)));

Здесь КвалификаторыЧисла(15, 2) — это 15 знаков всего и 2 после запятой. Для строки можно задать длину через КвалификаторыСтроки, для даты — состав (дата, время, дата и время).

Полный синтаксис метода: Колонки.Добавить(Имя, ТипыЗначений, Заголовок, Ширина). Третий параметр — заголовок для показа в форме, четвёртый — ширина колонки. Если таблица нужна только для расчёта и на форму не выводится, хватит первых двух.

Проверить, что колонка есть, можно методом Найти — он вернёт Неопределено, если колонки нет:

Если ТЗ.Колонки.Найти("Цена") = Неопределено Тогда
    ТЗ.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
КонецЕсли;

Добавляем строки и пишем в ячейки напрямую

Пока в таблице нет строк, заполнять нечего. Строку добавляет метод Добавить — он возвращает саму новую строку, и к её колонкам сразу обращаются через точку:

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

К той же ячейке можно обратиться и по индексу колонки, но имя надёжнее: при перестановке колонок индекс сломается, а имя — нет.

Если строку нужно вставить не в конец, а в определённую позицию, используйте Вставить с номером (нумерация с нуля):

ПерваяСтрока = ТЗ.Вставить(0);   // вставит в начало
ПерваяСтрока.Наименование = "Итого";

Заполнить уже существующую строку по её номеру тоже просто — обращение через индекс таблицы:

ТЗ[0].Количество = 100;          // первая строка
ТЗ[ТЗ.Количество() - 1].Цена = 0; // последняя строка

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

Заполняем колонку в цикле

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

Для Каждого Строка Из ТЗ Цикл
    Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;

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

Если внутри цикла нужен номер строки — например, для нумерации — берут цикл со счётчиком и обращаются по индексу:

Для Сч = 0 По ТЗ.Количество() - 1 Цикл
    ТЗ[Сч].НомерСтроки = Сч + 1;
КонецЦикла;

Скопировать данные из одной таблицы в колонку другой тоже удобно циклом — при условии, что строки идут в том же порядке:

Для Каждого Строка Из ТЗИсточник Цикл
    НоваяСтрока = ТЗПриёмник.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
КонецЦикла;

Метод ЗаполнитьЗначенияСвойств переносит значения всех одноимённых колонок за один вызов — не нужно перечислять каждую. Если имена колонок в источнике и приёмнике совпадают, это самый короткий способ скопировать строки.

Не удаляйте строки внутри прямого цикла Для Каждого: после удаления обход собьётся. Если строки надо и перебрать, и часть удалить, идите обратным циклом по счётчику: Для Сч = ТЗ.Количество() - 1 По 0 Цикл — и удаляйте ТЗ.Удалить(Сч).

Массовое заполнение одним методом

Когда во все строки колонки нужно положить одно и то же значение, цикл избыточен. Для этого есть метод ЗаполнитьЗначения:

ТЗ.ЗаполнитьЗначения(0, "Количество");

Первый параметр — значение, второй — колонки через запятую в одной строке. Можно перечислить несколько:

ТЗ.ЗаполнитьЗначения(0, "Количество, Сумма, Скидка");

Если второй параметр не указывать, значение запишется во все колонки всех строк — пользуйтесь этим осторожно.

Другая частая задача — загрузить в колонку готовый массив значений. Для этого есть ЗагрузитьКолонку:

МассивЦен = Новый Массив;
МассивЦен.Добавить(100);
МассивЦен.Добавить(200);
МассивЦен.Добавить(300);

ТЗ.ЗагрузитьКолонку(МассивЦен, "Цена");

Размер массива должен совпадать с числом строк в таблице. Если элементов в массиве меньше, чем строк, лишние строки получат Неопределено; если больше — часть данных просто не попадёт.

Обратный метод — ВыгрузитьКолонку — достаёт колонку в массив. Связка «выгрузил из одной таблицы → загрузил в другую» переносит колонку целиком:

Цены = ТЗИсточник.ВыгрузитьКолонку("Цена");
ТЗПриёмник.ЗагрузитьКолонку(Цены, "Цена");

Сравним способы:

СпособКогда применятьЧто учесть
Присваивание Строка.Колонка1–2 строки, точечная правкаСтрока должна существовать
Цикл Для Каждогозначение в каждой строке своёКолонка должна быть добавлена заранее
ЗаполнитьЗначенияодно значение во все строкиКолонки перечисляются одной строкой
ЗагрузитьКолонкуесть готовый массив данныхДлина массива = числу строк

Контроль типов и обработка ошибок

Если колонка описана типом, присваиваемое значение приводится к этому типу автоматически. Строка "5" в числовой колонке станет числом 5. Но если привести нельзя — например, в числовую колонку положить слово "пять" — вы получите не ошибку, а значение по умолчанию (для числа это 0). Расчёт тихо испортится.

Поэтому входные данные проверяйте до записи. Для разбора строки в число подойдёт попытка с перехватом:

Попытка
    Строка.Количество = Число(ВходноеЗначение);
Исключение
    Строка.Количество = 0;
    Сообщить("Не удалось распознать количество: " + ВходноеЗначение);
КонецПопытки;

Проверить тип уже лежащего в ячейке значения можно функцией ТипЗнч:

Если ТипЗнч(Строка.Цена) = Тип("Число") Тогда
    Строка.Сумма = Строка.Количество * Строка.Цена;
Иначе
    Строка.Сумма = 0;
КонецЕсли;

Отдельная ловушка — обращение к несуществующей колонке. Такой вызов прерывает выполнение с ошибкой «Поле объекта не обнаружено». Если имя колонки приходит извне (из настройки, из параметра), проверьте его через Колонки.Найти перед обращением, как показано в первом разделе.

Для составных данных задавайте колонке несколько типов сразу: Новый ОписаниеТипов("Число, Строка"). Тогда в ячейку можно класть и число, и строку, а приведение отключится.

Производительность на больших объёмах

На сотне строк разницы в способах нет. На десятках и сотнях тысяч строк выбор метода меняет время работы в разы.

Правила для больших таблиц:

  • 📦 Массовые методы вместо цикла. ЗаполнитьЗначения и ЗагрузитьКолонку работают заметно быстрее, чем присваивание в цикле строка за строкой. Если задача сводится к ним — используйте их.
  • 🔍 Индексы для поиска. Если вы многократно ищете строки методом Найти или НайтиСтроки, добавьте индекс по колонкам поиска: ТЗ.Индексы.Добавить("Наименование"). Без индекса поиск идёт перебором и на больших таблицах тормозит.
  • 🚫 Не ищите в цикле без индекса. Конструкция «для каждой строки одной таблицы ищу строку в другой» без индекса даёт квадратичную сложность: 10 000 строк превращаются в десятки миллионов сравнений.
  • 🧮 Считайте один раз. Не вызывайте ТЗ.Количество() в условии цикла по счётчику на огромных объёмах без нужды — сохраните в переменную, если таблица в цикле не меняется.

Для поиска и выборки по индексу используйте НайтиСтроки со структурой отбора — он вернёт массив подходящих строк:

ТЗ.Индексы.Добавить("Наименование");

Отбор = Новый Структура("Наименование", "Бумага А4");
Найденные = ТЗ.НайтиСтроки(Отбор);

Для Каждого Строка Из Найденные Цикл
    Строка.Количество = Строка.Количество + 1;
КонецЦикла;

Если из большой таблицы нужна только часть строк или колонок, не перебирайте всё вручную — метод Скопировать сразу вернёт новую таблицу по отбору:

ТЗТоваров = ТЗ.Скопировать(Отбор, "Наименование, Количество");

Заполнение колонки из запроса

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

Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ Номенклатура.Наименование КАК Наименование,
    |       Остатки.КоличествоОстаток КАК Количество
    |ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки() КАК Остатки";

ТЗ = Запрос.Выполнить().Выгрузить();

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

ТЗ.Колонки.Добавить("Дефицит", Новый ОписаниеТипов("Булево"));

Для Каждого Строка Из ТЗ Цикл
    Строка.Дефицит = (Строка.Количество <= 0);
КонецЦикла;

Если нужны не все строки результата, отбор удобнее задать в самом запросе условием ГДЕ, а не перебирать таблицу потом. База отфильтрует данные быстрее, и в память попадёт меньше строк.

Проверьте себя
Какой метод положит одно значение во все строки указанной колонки?
Что вернёт `ТЗ.Колонки.Найти("Цена")`, если такой колонки нет?
Что произойдёт при записи `"пять"` в числовую колонку с типом Число?
Как безопасно удалять часть строк при обходе таблицы?

Частые вопросы

Как добавить колонку в уже заполненную таблицу?
Вызовите ТЗ.Колонки.Добавить("НоваяКолонка") в любой момент. Во всех существующих строках новая ячейка получит значение по умолчанию (для типизированной колонки — значение её типа, для нетипизированной — Неопределено). Заполнить её потом можно циклом или ЗаполнитьЗначения.
Чем `ЗагрузитьКолонку` отличается от цикла?
Результат одинаковый, но ЗагрузитьКолонку переносит готовый массив за один вызов и работает быстрее на больших объёмах. Цикл нужен, когда значение каждой строки вычисляется, а не берётся из готового массива.
Почему в числовой колонке оказался 0, хотя я записал строку?
Колонка типизирована как Число, и платформа привела несовместимую строку к значению по умолчанию. Проверяйте и преобразуйте входные данные функцией Число() в блоке Попытка … Исключение до присваивания.
Как скопировать колонку из одной таблицы в другую?
Выгрузите её в массив методом ВыгрузитьКолонку("Имя") и загрузите в приёмник методом ЗагрузитьКолонку(Массив, "Имя"). Число строк в обеих таблицах должно совпадать.
Можно ли заполнить сразу несколько колонок одним значением?
Да. Перечислите их в одной строке через запятую: ТЗ.ЗаполнитьЗначения(0, "Количество, Сумма, Скидка"). Без второго параметра значение запишется во все колонки.