В разработке на платформе 1С:Предприятие 7.7 программисты часто сталкиваются с необходимостью конвертации данных между различными типами коллекций. Одной из классических задач является преобразование объекта СписокЗначений (СЗ) в объект ТаблицаЗначений (ТЗ). Казалось бы, задача тривиальная, но стандартный метод Выгрузить() имеет свои существенные ограничения, которые заставляют нас искать более изящные или производительные решения. Давайте подробно разберем, как сделать это "красиво" и максимально эффективно.
Начнем с того, что проанализируем штатное поведение платформы. Рассмотрим типичный код:
ТЗ = СоздатьОбъект("ТаблицаЗначений");
СЗ.Выгрузить(ТЗ);
В результате мы получим таблицу значений, состоящую ровно из одной колонки, в которую попадут только сами значения элементов списка. Однако в реальных задачах нам почти всегда требуется перенести также и представления элементов, а зачастую и состояние их пометок (если список используется для выбора). Стандартный метод Выгрузить() этого не умеет. Выясним, какие альтернативы у нас есть.
Это самый очевидный и понятный метод. Несмотря на то, что автор темы назвал его "некузявым", для небольших и средних списков он является эталоном надежности и читаемости кода. Разберем по шагам, как правильно реализовать такой перенос:
ТаблицаЗначений.Посмотрим на пример реализации:
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Значение");
ТЗ.НоваяКолонка("Представление");
ТЗ.НоваяКолонка("Пометка", "Число", 1, 0);
Для н = 1 По СЗ.РазмерСписка() Цикл
ТЗ.НоваяСтрока();
ТекЗнач = "";
ТекПредст = "";
ТекЗнач = СЗ.ПолучитьЗначение(н, ТекПредст);
ТЗ.Значение = ТекЗнач;
ТЗ.Представление = ТекПредст;
ТЗ.Пометка = СЗ.Пометка(н);
КонецЦикла;
Важный момент: использование переменной ТекПредст во втором параметре метода ПолучитьЗначение() позволяет нам за один вызов получить и объект, и его текстовое описание. Это чуть быстрее, чем вызывать отдельно ПолучитьЗначение и ПолучитьПредставление.
Если мы хотим "красоты" в коде, как в восьмой версии платформы, мы можем реализовать аналог функции ЗаполнитьЗначенияСвойств(). Проанализируем ситуацию: в 1С 8.х мы могли бы просто перебрать элементы коллекции и использовать системную функцию для копирования свойств. В "семерке" нам придется написать небольшую обертку в глобальном модуле.
Рассмотрим, как могла бы выглядеть такая функция, если бы мы писали ее один раз для всей системы:
Процедура глСЗвТЗ(СЗ, ТЗ) Экспорт
ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Значение");
ТЗ.НоваяКолонка("Представление");
Для н = 1 По СЗ.РазмерСписка() Цикл
ТЗ.НоваяСтрока();
Стр = "";
ТЗ.Значение = СЗ.ПолучитьЗначение(н, Стр);
ТЗ.Представление = Стр;
КонецЦикла;
КонецПроцедуры
Теперь в основном коде вызов будет выглядеть очень лаконично:
глСЗвТЗ(МойСписок, МояТаблица);
Если перед нами стоит задача обработать список из десятков тысяч элементов, обычный цикл в 1С 7.7 может работать медленно из-за особенностей интерпретатора. В этом случае мы можем прибегнуть к "черной магии" платформы — манипуляции с внутренним форматом объектов.
Проанализируем структуру: объекты СписокЗначений и ТаблицаЗначений при вызове ЗначениеВСтрокуВнутр() выдают текстовые блоки со схожей структурой заголовков. Теоретически, можно получить строку из СЗ, программно заменить идентификатор типа объекта в строке и загрузить ее обратно уже в объект ТЗ.
Внимание: это крайне опасный метод, так как структура внутренних строк не документирована и может отличаться в разных версиях движка (хотя в 7.7 она стабильна уже десятилетия). Разберем логику процесса:
ЗначениеВСтрокуВнутр(СЗ)."ValueList" на "ValueTable").ЗначениеИзСтрокиВнутр() для восстановления объекта в переменную типа ТЗ.На практике такой метод используется редко, так как он сложен в отладке, но по скорости выполнения он обгоняет цикл в разы, так как парсинг строки происходит на уровне скомпилированного ядра платформы.
Если ваша конфигурация использует библиотеку 1С++, то задача решается гораздо эффективнее. Класс ИндексированнаяТаблица предоставляет методы для прямой загрузки данных. Кроме того, часто данные в СЗ попадают из SQL-запроса. В таком случае правильнее будет вообще миновать этап создания СЗ и выгружать результат Recordset напрямую в ТЗ.
Посмотрим на пример, если мы используем возможности расширения платформы:
// Пример использования ИндексированнойТаблицы из 1С++
итз = СоздатьОбъект("ИндексированнаяТаблица");
итз.НоваяКолонка("Значение");
итз.НоваяКолонка("Представление");
// Далее используем методы быстрой загрузки или прямой работы с данными
Иногда нам не нужно физически переносить данные. Если цель конвертации — отобразить данные Списка Значений в табличном поле на форме с дополнительными колонками, мы можем применить следующий прием:
глПолучитьПредставлениеИзСЗ(ТекСписок, НомерСтроки).Этот метод позволяет экономить память, не дублируя данные Списка Значений в памяти еще и в виде полноценной Таблицы Значений. Выясним причину популярности этого метода: он обеспечивает мгновенный визуальный результат без тяжелых операций копирования.
Мы проанализировали различные подходы к задаче переноса данных из СЗ в ТЗ в 1С 7.7. Подведем итог:
Таким образом, выбор метода зависит от конкретного контекста задачи: объема данных и требований к чистоте кода в модуле.