При разработке и доработке конфигураций 1С разработчики нередко сталкиваются с задачей трансформации структуры данных: когда информация представлена в виде двумерной матрицы («шахматки») с переменным количеством колонок и строк, а сохранить или обработать её требуется в виде плоского списка строк в табличной части, содержащей всего один реквизит. Разберем, как правильно реализовать подобную выгрузку на встроенном языке 1С, выясним особенности работы стандартных методов и проанализируем архитектурные нюансы.
Представим ситуацию: у нас есть объект ТаблицаЗначений, количество колонок в котором заранее не зафиксировано (например, формируется динамически пользователем или выгружается из движений регистров). В целевой табличной части (ТабличнаяЧасть) определена всего одна колонка, допустим, Значение. Задача заключается в том, чтобы каждое отдельное значение из каждой ячейки таблицы значений было помещено в отдельную строку табличной части. Если в таблице значений 2 строки и 2 колонки, на выходе в табличной части должно быть создано 4 строки.
Рассмотрим базовое и самое прямое решение задачи. Чтобы извлечь данные из каждой ячейки, мы организуем вложенный цикл: внешний цикл будет обходить строки таблицы значений, а внутренний — коллекцию Колонки таблицы значений. Посмотрим на программную реализацию:
// Очищаем табличную часть перед заполнением
Объект.ТабличнаяЧасть.Очистить();
// Обходим все строки исходной таблицы значений
Для Каждого СтрТЗ Из ИсходнаяТЗ Цикл
// Для каждой строки обходим коллекцию колонок
Для Каждого ТекКолонка Из ИсходнаяТЗ.Колонки Цикл
ЗначениеЯчейки = СтрТЗ[ТекКолонка.Имя];
// Создаем новую строку в табличной части
НоваяСтрока = Объект.ТабличнаяЧасть.Добавить();
НоваяСтрока.Значение = ЗначениеЯчейки;
КонецЦикла;
КонецЦикла;
Разберем по шагам, как работает этот фрагмент кода:
Очистить() у табличной части, чтобы исключить наложение новых данных на старые.Для Каждого СтрТЗ Из ИсходнаяТЗ перебираем поочередно каждую строку.Для Каждого ТекКолонка Из ИсходнаяТЗ.Колонки позволяет обратиться к имени текущей колонки через свойство ТекКолонка.Имя.СтрТЗ[ТекКолонка.Имя] получает значение ячейки на пересечении текущей строки и колонки.Добавить() создает новую строку в табличной части, куда мы и записываем полученное значение.Построчное добавление элементов через метод Добавить() при большом количестве данных может работать медленно. Часто возникает вопрос: можно ли использовать платформенные методы ВыгрузитьКолонку() и ЗагрузитьКолонку()?
Проанализируем работу метода ЗагрузитьКолонку(): он принимает массив значений и последовательно перезаписывает указанную колонку в уже существующих строках табличной части, начиная с самого начала (с нулевого индекса). Если вызвать этот метод повторно для следующей колонки, данные будут не добавлены в конец, а перезапишут строки первой колонки. Поэтому напрямую вызывать ЗагрузитьКолонку() для каждой колонки ТЗ нельзя.
Однако мы можем сформировать единый одномерный Массив со всеми элементами и загрузить его за один вызов метода. Рассмотрим подробнее этот алгоритм:
ОбщийМассив = Новый Массив;
// Собираем значения всех ячеек в плоский массив
Для Каждого СтрТЗ Из ИсходнаяТЗ Цикл
Для Каждого ТекКолонка Из ИсходнаяТЗ.Колонки Цикл
ОбщийМассив.Добавить(СтрТЗ[ТекКолонка.Имя]);
КонецЦикла;
КонецЦикла;
// Очищаем табличную часть и загружаем массив целиком
Объект.ТабличнаяЧасть.Очистить();
Объект.ТабличнаяЧасть.ЗагрузитьКолонку(ОбщийМассив, "Значение");
Этот подход работает быстрее на больших объемах данных, так как создание строк и выделение памяти для табличной части оптимизировано внутри платформенного метода ЗагрузитьКолонку().
При решении подобных задач критически важно учитывать структуру самой базы данных и особенности платформы 1С:
Неопределено, NULL, системные типы или неподходящий тип ссылки), платформа не сможет сохранить такое значение либо оно очистится. Перед добавлением данных рекомендуется проверять их тип через ТипЗнч().Соответствие:
УникальныеЗначения = Новый Соответствие;
Для Каждого СтрТЗ Из ИсходнаяТЗ Цикл
Для Каждого ТекКолонка Из ИсходнаяТЗ.Колонки Цикл
ТекЗначение = СтрТЗ[ТекКолонка.Имя];
// Фильтруем пустые значения и исключаем дубликаты
Если ЗначениеЗаполнено(ТекЗначение) Тогда
УникальныеЗначения.Вставить(ТекЗначение, Истина);
КонецЕсли;
КонецЦикла;
КонецЦикла;
// Заполняем табличную часть только уникальными элементами
Объект.ТабличнаяЧасть.Очистить();
Для Каждого КлючИЗначение Из УникальныеЗначения Цикл
НоваяСтрока = Объект.ТабличнаяЧасть.Добавить();
НоваяСтрока.Значение = КлючИЗначение.Ключ;
КонецЦикла;
Нередко программисты прибегают к созданию такой одноколоночной табличной части в документе (например, в документе «Корректировка регистров»), чтобы включить реквизит табличной части в состав Критерия отбора и отображать документ в связанных ссылках справочника. Разберем, почему создание отдельной табличной части здесь часто является избыточным решением:
Регистратор. Включать искусственную табличную часть в документ не требуется.Таким образом, для простой технической трансформации данных оптимально использовать двойной цикл либо пакетную загрузку через массив, контролируя совпадение типов и исключая дублирование данных.