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