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