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