1C-Code → Основы 1С → Таблица значений
Коллекция, которой в 1С пользуются чаще массива. Колонки, строки, поиск, свёртка и итоги — и почему её нельзя отдать на клиент.
Глава 34 из 36 · чтение примерно 26 минут
Массив хранит список значений. Соответствие — пары ключ-значение. А таблица значений хранит строки с колонками — то есть ровно то, чем в учётной работе оказывается почти всё: строки документа, результат запроса, промежуточный расчёт.
Поэтому она встречается чаще остальных коллекций вместе взятых.
Т = Новый ТаблицаЗначений;
Т.Колонки.Добавить("Товар");
Т.Колонки.Добавить("Сумма");
Строка = Т.Добавить();
Строка.Товар = "Гвозди";
Строка.Сумма = 120;
| Часть | Что означает |
|---|---|
Новый ТаблицаЗначений | пустая таблица без колонок |
Колонки.Добавить | колонки заводят до строк, иначе писать некуда |
Т.Добавить() | добавляет строку и ВОЗВРАЩАЕТ её — сразу заполняем |
Строка.Товар = | к полям строки обращаются по имени колонки |
Большая часть работы с таблицей — это не циклы, а её собственные методы. Писать цикл там, где есть метод, — и дольше, и медленнее.
Итог(Колонка) — сумма по колонке. Свернуть(Группировки, Суммы) — схлопнуть строки, сложив числовые колонки. НайтиСтроки(Отбор) — все строки по условию. Найти(Значение, Колонки) — первая подходящая. Сортировать(Колонки) — порядок. ВыгрузитьКолонку(Имя) — массив значений одной колонки.
Свернуть — самый полезный метод
Т.Свернуть("Товар", "Сумма") превращает десять строк по одному товару в одну со сложенной суммой.
Это ровно то, что в запросе делает СГРУППИРОВАТЬ ПО, только для таблицы, которая уже в памяти. Перед соединением или выводом свёртка часто убирает половину строк — и заодно половину проблем с дублями.
Руками их создают реже, чем получают готовыми:
Запрос.Выполнить().Выгрузить() — результат запроса таблицей. Документ.Товары.Выгрузить() — табличная часть документа. Т.Скопировать() — копия, которую можно портить, не трогая исходную.
Скопировать с параметрами умеет сразу отобрать строки и оставить нужные колонки — часто это дешевле, чем цикл с удалением.
На чём спотыкаются
Удаляют строки в цикле «Для Каждого». После удаления обход сбивается и часть строк пропускается. Удалять надо с конца — от последней строки к первой. Обратного Для в 1С нет, поэтому пишут Сч = Т.Количество() - 1; Пока Сч >= 0 Цикл … Сч = Сч - 1;: подробно это разобрано в главе про коллекции.
Пытаются передать таблицу на клиент. ТаблицаЗначений — серверный тип, на клиенте его нет. Для передачи есть ЗначениеВСтрокуВнутр, адресное хранилище или реквизит формы с типом «ТаблицаЗначений».
Складывают строки циклом вместо Итог и Свернуть. Работает, но длиннее и медленнее — а на большой таблице заметно.
Забывают, что Добавить возвращает строку. Пишут Т.Добавить(); Строка = Т[Т.Количество()-1]; — то же самое в три раза длиннее.
Упражнение 1. Итог по таблице
Функция ИтогПоСтрокам(Суммы) получает массив чисел, складывает их в таблицу значений с одной колонкой Сумма и возвращает итог по этой колонке — методом Итог, а не циклом.
Решается в тренажёре: код запускается и проверяется сразу.
Т.Колонки.Добавить("Сумма") заводит колонку, в которую можно положить и число, и строку, и Неопределено. Это удобно ровно до первой ошибки.
Дальше начинается то, что ищут полдня: Итог("Сумма") падает, потому что в одной строке оказалась строка «120» вместо числа; сортировка выстраивает числа как текст, и 9 оказывается больше 100; сравнение с нулём не срабатывает, потому что там Неопределено.
Поэтому колонке задают тип.
Т.Колонки.Добавить("Товар", Новый ОписаниеТипов("Строка"));
Т.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
Т.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
Строка = Т.Добавить(); // Количество уже 0, а не Неопределено
| Часть | Что означает |
|---|---|
Новый ОписаниеТипов("Число") | описание типа колонки — второй параметр Добавить |
Количество уже 0 | у типизированной колонки есть пустое значение: 0, пустая строка, пустая дата |
Неопределено | а в нетипизированной новая ячейка именно такая — и любая арифметика по ней падает |
Откуда типы берутся сами
Таблицу, полученную из запроса (Выгрузить()) или из табличной части документа, типизировать не надо: колонки уже описаны — типами полей базы.
Задавать типы приходится там, где таблицу собирают руками: промежуточный расчёт, разбор файла, сбор результата для отчёта. Там же и ошибаются.
Два метода, и путают их постоянно.
НайтиСтроки(Отбор) — все строки, подходящие под отбор. Отбор задаётся структурой «колонка → значение», условий может быть несколько. Возвращает массив: не нашлось — массив пустой, проверять надо Количество() = 0, а не сравнение с Неопределено.
Найти(Значение, Колонки) — первая строка, где значение встретилось. Возвращает строку или Неопределено. Колонки перечисляют через запятую одной строкой; не указать их — значит искать по всем.
Найденная строка — та же самая строка
НайтиСтроки возвращает не копии, а ссылки на строки самой таблицы. Написали Найденная.Сумма = 0 — изменилась таблица.
Это рабочий приём: найти строку по отбору и поправить её на месте, без индексов и циклов. И это же ловушка, когда правку не задумывали: «я же работал с результатом поиска, откуда изменения в таблице».
Упражнение 2. Сумма по складу
Напишите СуммаСклада(Т, Склад): таблица с колонками Товар, Склад, Сумма. Верните сумму строк указанного склада — через НайтиСтроки, а не перебором всей таблицы. Проверка спрашивает про «Основной» и ждёт 2550.
Решается в тренажёре: код запускается и проверяется сразу.
Т.Свернуть("Товар", "Сумма") — два аргумента, и оба списки колонок через запятую: по чему группировать и что складывать.
Главное, чего не ждут: колонки, не попавшие ни в первый список, ни во второй, из таблицы исчезают. Была таблица с товаром, складом и суммой, свернули по товару с суммой — колонки «Склад» больше нет. Не «стала пустой», а нет: обращение к ней падает.
Это логично — склад у схлопнутых строк был разный, и выбрать один невозможно, — но узнают об этом обычно из ошибки.
Свернуть, Сортировать и Удалить меняют саму таблицу
Они ничего не возвращают. Новая = Т.Свернуть(…) не сработает: Новая окажется Неопределено, а исходная таблица будет уже свёрнута.
Когда исходная нужна целой, работают с копией: К = Т.Скопировать(); и сворачивают уже её. Особенно это важно, когда таблицу вам передали в параметре — она чужая, и свернув её, вы испортите данные вызывающему.
Упражнение 3. Сколько разных товаров
Напишите СколькоТоваров(Т): сколько различных товаров встречается в таблице. Сделайте это свёрткой копии — исходная таблица должна остаться целой. В проверке четыре строки и три разных товара.
Решается в тренажёре: код запускается и проверяется сразу.
Упражнение 4. Самый дорогой товар
Напишите СамыйДорогой(Т): верните название товара с наибольшей суммой по всем складам. Сложите суммы свёрткой, отсортируйте по убыванию и возьмите первую строку. Ответ проверки — Гвозди.
Решается в тренажёре: код запускается и проверяется сразу.
Скопировать умеет больше, чем копировать. У него два необязательных аргумента: отбор и список колонок.
К = Т.Скопировать(); // всё целиком
К = Т.Скопировать(Новый Структура("Склад", Склад)); // только строки склада
К = Т.Скопировать(, "Товар, Сумма"); // только две колонки
К = Т.Скопировать(Отбор, "Товар, Сумма"); // и то, и другое
| Часть | Что означает |
|---|---|
Скопировать() | новая таблица с теми же строками; менять её можно свободно |
Новый Структура("Склад", Склад) | отбор — та же структура, что у НайтиСтроки |
, "Товар, Сумма" | запятая на месте пропущенного отбора — колонки идут вторым аргументом |
короче цикла | то же самое циклом с Удалить — десять строк и шанс ошибиться в индексах |
Т.ВыгрузитьКолонку("Товар") возвращает массив значений одной колонки — по строке на элемент, в том же порядке и с дублями.
Нужен он в основном для одного: сложить в параметр запроса. Достали таблицу, выгрузили колонку ссылок, отдали её в &СписокТоваров — и получили данные по всем разом, вместо запроса в цикле.
Обратный метод — ЗагрузитьКолонку(Массив, "Имя"): записывает значения массива в колонку по порядку. Встречается редко, но в чужом коде попадается.
Когда таблица большая
НайтиСтроки перебирает таблицу целиком. На сотне строк это незаметно, на десятках тысяч — и особенно внутри цикла — превращается в минуты.
В настоящей 1С у таблицы есть индексы: Т.Индексы.Добавить("Товар") — и поиск по этой колонке идёт сразу к нужным строкам. Наш браузерный движок индексов не изображает, поэтому упражнения на них нет; знать про них всё равно нужно — про них спрашивают.
И более общий приём: если ищете по таблице много раз, разложите её один раз в соответствие «ключ → строка». Тот же способ, что в главе про коллекции.
Это спрашивают на собеседованиях, и ответ «потому что серверный тип» неполный.
Таблица значений несериализуема: её нельзя превратить в набор байтов и обратно, а именно это происходит при передаче между сервером и клиентом. Поэтому серверная функция, вернувшая таблицу на клиент, падает.
Что делают вместо этого:
Массив структур — когда данных немного и они нужны коду. Сериализуется, передаётся, читается так же.
Реквизит формы типа ТаблицаЗначений — когда данные нужно показать. На форме таблица живёт законно, а обмен с сервером делают ЗначениеВРеквизитФормы и РеквизитФормыВЗначение.
Адресное хранилище — когда таблица большая и её надо просто «пронести» через клиент, не читая: положили на сервере, передали адрес, забрали на сервере.
ЗначениеВСтрокуВнутр — не способ
В старом коде встречается передача через ЗначениеВСтрокуВнутр: таблица превращается в строку, едет на клиент и там разворачивается обратно.
Формат этой строки не описан и меняется между версиями платформы. Хранить её в базе нельзя, разбирать нельзя, на совместимость рассчитывать нельзя. Для передачи есть три способа выше — они и предназначены для этого.
У таблицы есть два ближайших родственника, которые встречаются реже, но узнавать их надо.
ДеревоЗначений — та же таблица, но у строк есть подчинённые строки (Строка.Строки). Так показывают иерархию: группы и элементы, итоги и детали отчёта.
ТаблицаЗначений на форме — это уже не она, а данные формы: у них свои типы и своё поведение, и напрямую методы таблицы там не работают. Отсюда и РеквизитФормыВЗначение: получить настоящую таблицу, поработать, положить обратно.
Таблица значений — строки с колонками, самая частая коллекция в 1С. Колонки заводят до строк; Добавить() сразу возвращает строку. Итог, Свернуть, НайтиСтроки, Сортировать делают работу за вас — цикл нужен реже, чем кажется. Удалять строки обходом «Для Каждого» нельзя. На клиент таблицу не передать: это серверный тип.
Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.