Есть обработка, в которой есть дерево с большим количеством строк(несколько десятков тысяч). При выполнении процедуры мне возвращается в качестве параметра массив структур, в которых содержится значение, которое нужно искать в дереве.
Пытался сделать следующим образом: Передавал массив структур на сервер. Там дополнял структуры индексами строк дерева, в которых находилось нужное значение. Но на деле оказалось, что индекс строки дерева не то же самое, что идентификатор строки дерева формы.
 Т.е. если я пытаюсь выполнить Элементы.Дерево.ТекущаяСтрока = Индекс то активируется совсем не та строка, что я нашел с индексом в дереве значений.
 Просто выполнить всё на сервере не могу, так при этом собьются идентификаторы строк дерева формы, а для дальнейшей работы это критично.
 Вопросы: Можно ли как-то организовать поиск на клиенте по дереву значений без полного перебора строк? Как?
 Как получить идентификатор строки дерева значений на клиенте?

Код, который использовал на данный момент(неправильно возвращает идентификатор строки):

```1c
    &НаКлиенте
    Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    ...
    ЗаполнитьИндексыСтрокДерева(Параметр.СозданныеОбъекты);
    Для каждого стр Из Параметр.СозданныеОбъекты Цикл// Параметр.СозданныеОбъекты - массив структур
    Элементы.ДеревоНоменклатуры.ТекущаяСтрока = стр.Индекс;
    ТекущиеДанные = Элементы.ДеревоНоменклатуры.ТекущиеДанные;
    Элементы.ДеревоНоменклатуры.ТекущиеДанные.Номенклатура = стр.Номенклатура;
    КонецЦикла;
    ...
    КонецПроцедуры

    &НаСервере
    Процедура ЗаполнитьИндексыСтрокДерева(СозданныеОбъекты)
    Дерево = РеквизитФормыВЗначение("ДеревоНоменклатуры");
    Для каждого стр Из СозданныеОбъекты Цикл
    строкаДерева = Дерево.Строки.Найти(стр.ИдентификаторНоменклатуры,"ИдентификаторНоменклатурыОблака");
    стр.Вставить("Индекс", Дерево.Строки.Индекс(строкаДерева));
    КонецЦикла;
    КонецПроцедуры
```
---
Другой вариант:

```1c
    &НаКлиенте
    Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    ...
    ЗаполнитьНоменклатуруДерева(Параметр.СозданныеОбъекты);

    ...
    КонецПроцедуры

    &НаСервере
    Процедура ЗаполнитьНоменклатуруДерева(СозданныеОбъекты )

    Дерево = РеквизитФормыВЗначение("ДеревоНоменклатуры");
    Для каждого стр Из СозданныеОбъекты Цикл
    СтрокаДереваЗначений = Дерево.Строки.Найти(стр.ИдентификаторНоменклатуры,"ИдентификаторНоменклатурыОблака");

    СтэкУровнейДерева = Новый Массив;

    Пока СтрокаДереваЗначений.Родитель <> Неопределено Цикл

    КоллекцияТекущейСтроки = СтрокаДереваЗначений.Родитель.Строки;

    ИндексТекущейСтроки = КоллекцияТекущейСтроки.Индекс(СтрокаДереваЗначений);

    СтэкУровнейДерева.Вставить(0, ИндексТекущейСтроки);

    СтрокаДереваЗначений = СтрокаДереваЗначений.Родитель;

    КонецЦикла;

    ИдентификаторПозиции = Неопределено;
    ТекущаяКоллекцияФормы = ДеревоНоменклатуры.ПолучитьЭлементы()[0].ПолучитьЭлементы();

    Для Каждого Индекс Из СтэкУровнейДерева Цикл

    ТекущаяСтрока = ТекущаяКоллекцияФормы[Индекс];
    ИдентификаторПозиции = ТекущаяСтрока.ПолучитьИдентификатор();

    ТекущаяКоллекцияФормы = ТекущаяСтрока.ПолучитьЭлементы();

    КонецЦикла;

    Элементы.ДеревоНоменклатуры.ТекущаяСтрока = ИдентификаторПозиции;
    ТекСтрока = ДеревоНоменклатуры.НайтиПоИдентификатору(Элементы.ДеревоНоменклатуры.ТекущаяСтрока);
    ТекСтрока.Номенклатура = стр.Номенклатура;
    КонецЦикла;
    КонецПроцедуры
```
---
Другой вариант:

```1c
    &НаКлиенте
    Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    ...
    ЗаполнитьНоменклатуруДерева(Параметр.СозданныеОбъекты);

    ...
    КонецПроцедуры

    &НаСервере
    Процедура ЗаполнитьНоменклатуруДерева(СозданныеОбъекты )

    Дерево = РеквизитФормыВЗначение("ДеревоНоменклатуры");
    Для каждого стр Из СозданныеОбъекты Цикл
    СтрокаДереваЗначений = Дерево.Строки.Найти(стр.ИдентификаторНоменклатуры,"ИдентификаторНоменклатурыОблака");

    СтэкУровнейДерева = Новый Массив;

    Пока СтрокаДереваЗначений.Родитель <> Неопределено Цикл

    КоллекцияТекущейСтроки = СтрокаДереваЗначений.Родитель.Строки;

    ИндексТекущейСтроки = КоллекцияТекущейСтроки.Индекс(СтрокаДереваЗначений);

    СтэкУровнейДерева.Вставить(0, ИндексТекущейСтроки);

    СтрокаДереваЗначений = СтрокаДереваЗначений.Родитель;

    КонецЦикла;

    ИдентификаторПозиции = Неопределено;
    ТекущаяКоллекцияФормы = ДеревоНоменклатуры.ПолучитьЭлементы()[0].ПолучитьЭлементы();

    Для Каждого Индекс Из СтэкУровнейДерева Цикл

    ТекущаяСтрока = ТекущаяКоллекцияФормы[Индекс];
    ИдентификаторПозиции = ТекущаяСтрока.ПолучитьИдентификатор();

    ТекущаяКоллекцияФормы = ТекущаяСтрока.ПолучитьЭлементы();

    КонецЦикла;

    Элементы.ДеревоНоменклатуры.ТекущаяСтрока = ИдентификаторПозиции;
    ТекСтрока = ДеревоНоменклатуры.НайтиПоИдентификатору(Элементы.ДеревоНоменклатуры.ТекущаяСтрока);
    ТекСтрока.Номенклатура = стр.Номенклатура;
    КонецЦикла;
    КонецПроцедуры
```
---
Один из вариантов - на сервере когда заполняется дерево - заполняешь отдельную таблицу значений.
В таблице значений 2 колонки:
1 - ИдентификаторНоменклатуры
2 - идентификатор строки дерева

Добавить индекс по ИдентификаторНоменклатуры.

Таблицу хранить во временном хранилище. То есть в реквизите формы хранить только адрес.
Когда нужно искать - получать из хранилища и искать в таблице.
В форме не использовать ЗначениеВРеквизитФормы для дерева значений, после того как таблица заполнена, иначе идентификаторы все слетят.
При добавлении новых строк в дерево, надо добавлять строки и в таблицу.
---
Непонятно, что за индекс, но в УФ в ДанныеФормыДерево, ДанныеФормыКоллекция строки идентифицируются не по номеру строки или ещё какому-то реквизиту, а по их внутреннему идентификатору строки.
Если добавить строку, удалить строку и снова добавить строку, то у второй добавленной строки идентификатор строки будет отличаться от первой добавленной строки.
Идентификаторы строки пересчитываются по порядку платформой при создании формы, если я правильно помню.
Поэтому нужно использовать методы ПолучитьИдентификатор() и НайтиПоИдентификатору(<Идентификатор>)

Зачем гонять форму со всеми данными на сервер и обратно ради заполнения строк дерева? Прямого поиска, как в таблице, у ДанныеФормыДерево нет, только через рекурсивный перебор строк (доступно НаКлиенте). И скорее всего будет быстрее всего, вряд ли у дерева большая вложенность.
Пример рекурсии можно посмотреть вот тут, в том числе в комментариях: https://infostart.ru/1c/articles/931494
---
На клиенте идентификаторы строк можно получить с помощью ПолучитьИдентификатор(), например,так:

```1c
    ...............
    СписокИдентифиаторов = Новый СписокЗначений;
    ПолучитьИдентификатры(ДеревоНоменклатуры.ПолучитьЭлементы(), СписокИдентифиаторов);
    Для Каждого а из СписокИдентифиаторов Цикл
    Стр = ДеревоНоменклатуры.НайтиПоИдентификатору(а.Значение);
    КонецЦикла;
    ............
    &НаКлиенте
    Процедура ПолучитьИдентификатры(СтрокиДерева, СписокИдентифиаторов)
    Для Каждого Стр из СтрокиДерева Цикл
    Ид = Стр.ПолучитьИдентификатор();
    СписокИдентифиаторов.Добавить(Ид);
    ПолучитьИдентификатры(Стр.ПолучитьЭлементы(), СписокИдентифиаторов);
    КонецЦикла;
    КонецПроцедуры
```
---
Тоже за полный перебор рекурсией, хоть бы 10 тыс. строк.
---
В веб.клиенте это будет долгое и печальное зрелище)
---
Дольше, чем перегнать все данные формы на сервер и обратно? Это фактически равно двойному перебору всего дерева, хоть и средствами платформы.
---
Так они есть на сервере, эти данные. Там же не всегда одно и тоже при каждом серверном вызове передается, по крайней мере я на это надеюсь)
---
увы: https://its.1c.ru/db/pubv8devui#content:192:hdoc
Когда из клиентской процедуры/функции вызывается серверная (с директивой компиляции &НаСервере) процедура/функция, происходит передача всей формы на сервер.
Сначала контекст формы (реквизиты формы, элементы формы, параметры формы) специальным образом упаковываются и подготавливаются к передаче на сервер.
На сервере создается серверная часть формы, полученный контекст разворачивается в данные, элементы и реквизиты формы, после чего инициализируется модуль формы.
После того как программный объект формы будет полностью подготовлен к работе, выполняется код той процедуры/функции, которая была вызвана на клиенте.
После того как исполнение кода на сервере будет закончено, происходит обратное действие. Контекст формы упаковывается и передается обратно на клиент, в клиентскую часть формы.
На клиенте происходит синхронизация контекста формы: имеющийся контекст заменяется контекстом, полученным с сервера. А на сервере происходит уничтожение серверной части формы.
---
Там в самом низу есть пояснения:
Во-вторых, контекстные вызовы сервера мы рассматривали упрощенно. На самом деле платформа очень серьезно оптимизирует объем данных, передаваемых между клиентом и сервером. Например, при открытии формы на клиент передается только часть данных, которую видит в данный момент пользователь. С клиента на сервер передаются не все данные, а только те, которые были изменены на клиенте. При изменении данных на сервере на клиент также передаются не все данные, а только те, которые были изменены.

Поэтому если на сервере требуется обращаться к большому массиву данных, которые хранятся в данных формы, то выгоднее выполнить именно контекстный серверный вызов. Потому что в этом случае платформа эффективно выполнит передачу данных формы на сторону сервера, там сконструирует соответствующий контекст и обратно перешлет данные на клиент.
---
действительно, не всё прочитал.
---
Почему?
---
Перебор коллекций в вебе выполняется очень медленно. По крайней раньше так было.
Например, таблица в 1000 строк, и надо изменить всем строкам отметку - в вебе выполнить это на сервере быстрее, чем на клиенте перебор.
---
видимо веб-клиент не очень производительная штука. ТС-у остаётся реализовать все варианты и потестить скорость каждого.
---
Так может как раз из-за неявных серверных вызовов, когда не все данные были на клиенте. Это вроде бы лечится, если коллекцию формировать не ПриСозданииНаСервере, а например в ПриОткрытии.
---
Приложил обработку для тестирования.
В дереве в общей сложности 45000 элементов. На тонком клиенте и веб-клиенте в клиент-серверной базе поиск на клиенте выигрывает.
В файловой на тонком клиенте выигрывает сервер.
Если предложите более оптимальный способ поиска на сервере, готов проверить. Возможно, стоит использовать соответствие соответствий вместо индексированной ТЗ.
---
Спасибо, коллеги, за активную помощь! Удалось победить с помощью (1). Правда, в моём случае надо было искать только в верхнем уровне дерева. Код, в итоге, использовал такой:

```1c
    &НаСервере
    Процедура ЗаполнитьИндексыСтрокДерева(СозданныеОбъекты)
    Дерево = РеквизитФормыВЗначение("ДеревоНоменклатуры");
    Для каждого стр Из СозданныеОбъекты Цикл
    строкаДерева = Дерево.Строки.Найти(стр.ИдентификаторНоменклатуры,"ИдентификаторНоменклатурыОблака");
    стр.Вставить("Индекс", Дерево.Строки.Индекс(строкаДерева));

    КонецЦикла;
    КонецПроцедуры

    &НаКлиенте
    Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    ....
    ЗаполнитьИндексыСтрокДерева(Параметр.СозданныеОбъекты);
    Для каждого стр Из Параметр.СозданныеОбъекты Цикл
    Элементы.ДеревоНоменклатуры.ТекущаяСтрока = стр.Индекс;
    ТекущиеДанные = Элементы.ДеревоНоменклатуры.ТекущиеДанные;
    Элементы.ДеревоНоменклатуры.ТекущиеДанные.Номенклатура = стр.Номенклатура;
    КонецЦикла;
    ....
    КонецПроцедуры
```

В строках стало всё корректно заполняться, но столкнулся со следующей проблемой:
во время выполнения процедуры появляется сообщение: "Неизвестный идентификатор формы".
http://snap.ashampoo.com/AqcSYGEL7mfQmcTxKONRt6pSEr6IoQKOcMC2ThHstneApyX1HGM3e5Oj­oMR685mA
Раньше я его не наблюдал. Что я делаю не так?
---
Извините, неверную процедуру указал. Правильная ниже:

```1c
    &НаСервере
    Процедура ЗаполнитьИндексыСтрокДерева(СозданныеОбъекты)
    Дерево = РеквизитФормыВЗначение("ДеревоНоменклатуры");
    Для каждого стр Из СозданныеОбъекты Цикл
    строкаДерева = Дерево.Строки.Найти(стр.ИдентификаторНоменклатуры,"ИдентификаторНоменклатурыОблака");
    Индекс = Дерево.Строки.Индекс(строкаДерева);
    ТекущаяСтрокаДерева = ДеревоНоменклатуры.ПолучитьЭлементы()[Индекс];
    ИдентификаторПозиции = ТекущаяСтрокаДерева.ПолучитьИдентификатор();
    стр.Вставить("Индекс", ИдентификаторПозиции);

    КонецПроцедуры
```

Проблема с неизвестным идентификатором формы по-прежнему актуальна.
---
Зачем это все?
У вас уже в процедуре ЗаполнитьИндексыСтрокДерева есть строка дерева формы ТекущаяСтрокаДерева - так заполните в ней номенклатуру и все. Не надо на клиент это тащить.

```1c
    &НаСервере
    Процедура ЗаполнитьИндексыСтрокДерева(знач СозданныеОбъекты)
    Дерево = РеквизитФормыВЗначение("ДеревоНоменклатуры");
    Для каждого стр Из СозданныеОбъекты Цикл
    строкаДерева = Дерево.Строки.Найти(стр.ИдентификаторНоменклатуры,"ИдентификаторНоменклатурыОблака");
    Индекс = Дерево.Строки.Индекс(строкаДерева);
    ТекущаяСтрокаДерева = ДеревоНоменклатуры.ПолучитьЭлементы()[Индекс];
    ТекущаяСтрокаДерева.Номенклатура = стр.Номенклатура;
    КонецЦикла;
    КонецПроцедуры
```
---
Да я и так, и так пробовал. Работает. Но вот с идентификатором формы проблема возникла. Думал, в каком-то из вариантов она исчезнет.
---
Комментируйте код частями, может получится понять что-то)
---
Почитал про эту ошибку. Причина - открытие несколько экземпляров формы обработки в одном сеансе.
(23) У меня, получается, вызывается блок типового кода по загрузке номенклатуры из облака:

```1c
    &НаКлиенте
    Процедура ЗагрузитьНоменклатуру(ИдентификаторыНоменклатуры, ЗакрытьФормуПослеСоздания = Ложь)

    //НастроитьФормуПриДлительнойОперации(Истина);

    ПараметрыЗавершения = Новый Структура;
    ПараметрыЗавершения.Вставить("ИдентификаторЗадания", Неопределено);
    ПараметрыЗавершения.Вставить("ЗакрытьФормуПослеСоздания", ЗакрытьФормуПослеСоздания);

    Оповещение = Новый ОписаниеОповещения("ЗагрузкаДанныхПродолжение",
    ЭтотОбъект, ПараметрыЗавершения);

    РаботаСНоменклатуройКлиент.ЗагрузитьНоменклатуруИХарактеристики(
    Оповещение,
    ИдентификаторыНоменклатуры,
    ЭтотОбъект,
    Неопределено,
    Неопределено,
    Неопределено);//Элементы.СтраницыДлительнойОперации);

    КонецПроцедуры

    &НаКлиенте
    Процедура ЗагрузкаДанныхПродолжение(Результат, ДополнительныеПараметры) Экспорт

    //НастроитьФормуПриДлительнойОперации(Ложь);

    //Если Результат = Неопределено Тогда
    // ОбновитьПризнакЗагрузки();
    // Возврат;
    //КонецЕсли;
    //
    //Если ДополнительныеПараметры.ЗакрытьФормуПослеСоздания Тогда
    // Закрыть(Новый Структура("ВыбранныеОбъекты", Результат.НовыеЭлементы));
    // Возврат;
    //КонецЕсли;

    //УстановитьВидимостьДоступность();

    КонецПроцедуры
```

И он, вроде бы, должен "понимать", что в массиве "ИдентификаторыНоменклатуры" несколько элементов. Но, почему-то, он пытается последовательно открывать форму создания видов номенклатуры вместо того, чтобы показать форму группового создания видов номенклатуры(иногда, впрочем, и её показывает - не понял, по какому принципу). Примеров вызова групповой загрузки из облака в базе не нашел. На работу моей обработки это не влияет, поэтому решил оставить всё как есть.