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