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