Как правильно проверить наличие выбранных строк в таблице в 1С 8.3

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

В процессе доработки конфигураций 1С разработчикам нередко приходится сталкиваться с кодом предшественников, который выглядит избыточным, содержит скрытые ошибки или работает неоптимально. Рассмотрим типовую ситуацию: в коде выполняется выгрузка таблицы и обход строк для выяснения, выбрана ли пользователем хотя бы одна позиция. Выясним, почему нельзя просто заменить такой алгоритм проверкой общего количества строк, какие ошибки таит исходный фрагмент и как написать лаконичный, быстрый и надежный код.

Проанализируем исходный фрагмент, вызвавший вопросы:


ТабТовары = ТаблицаТоваров.Выгрузить();
Для Каждого Стр Из ТабТовары Цикл
    Стр.НомерСтроки = Индекс + 1;
    Если Стр.Выбран Тогда
        ЕстьТовары = Истина;
    КонецЕсли;
КонецЦикла;
Если Не ЕстьТовары Тогда
    Возврат Ложь;
КонецЕсли;

Анализ логики и скрытые ошибки исходного кода

Разберем по шагам, почему данный участок кода написан некорректно и какие проблемы он в себе несет:

  1. Подмена бизнес-логики: попытка заменить этот код условием Если ТаблицаТоваров.Количество() = 0 Тогда Возврат Ложь; КонецЕсли; в корне неверна. Общее число записей в таблице не гарантирует, что пользователь установил флаг пометки хотя бы для одной строки. Если в документе 10 строк, но ни одна из них не отмечена (Стр.Выбран = Ложь), простая проверка количества вернет Истина, что приведет к логической ошибке выполнения программы.
  2. Бессмысленное копирование данных в памяти: вызов ТаблицаТоваров.Выгрузить() создает полную копию коллекции в оперативной памяти. Изменения, вносимые в таблицу ТабТовары (в частности, перезапись номера строки), никак не отражаются на исходном объекте ТаблицаТоваров. После завершения процедуры созданная копия просто уничтожается сборщиком мусора, создавая лишнюю нагрузку на сервер или клиентский сеанс.
  3. Проблема с переменной индекса: в платформе 1С цикл Для Каждого не объявляет и не увеличивает переменную Индекс автоматически. Если переменная не была определена заранее, платформа выбросит исключение времени выполнения «Переменная не определена». Если же она была объявлена вне цикла и не меняется внутри него, всем строкам присвоится одинаковый номер. Для получения индекса текущей строки без счетчиков используется вызов ТаблицаТоваров.Индекс(Стр).
  4. Отсутствие инициализации переменной: флаг ЕстьТовары должен быть явно определен до начала цикла (ЕстьТовары = Ложь;). Если флаг не инициализирован, а условие Стр.Выбран ни разу не сработало, попытка проверки Если Не ЕстьТовары завершится ошибкой выполнения.
  5. Отсутствие раннего выхода: обход продолжается по всей таблице до самого конца, даже если первая же строка оказалась выбранной. Здесь явно не хватает оператора Прервать.

Вариант 1. Оптимальный обход по исходной коллекции (универсальный)

Если требуется определить лишь факт наличия хотя бы одного элемента с установленным признаком, наиболее производительным и универсальным решением является прямой цикл по исходной коллекции с немедленным выходом из функции. Посмотрим на пример реализации:


Функция ЕстьВыбранныеТовары(ТаблицаТоваров)
    
    Для Каждого Стр Из ТаблицаТоваров Цикл
        Если Стр.Выбран Тогда
            Возврат Истина;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Ложь;
    
КонецФункции

Данный вариант обладает ключевыми преимуществами:

  1. Универсальность: код одинаково стабильно работает с объектами типов ТаблицаЗначений, ТабличнаяЧасть и ДанныеФормыКоллекция (на клиенте в управляемых формах).
  2. Экономия ресурсов: не происходит дублирования данных в оперативной памяти через метод Выгрузить().
  3. Максимальное быстродействие: как только встречается первая подходящая строка, функция сразу завершает работу, не обрабатывая оставшиеся сотни или тысячи записей.

Вариант 2. Использование метода «Найти» для ТаблицыЗначений

Если коллекция гарантированно является объектом типа ТаблицаЗначений, можно использовать встроенный платформенный поиск. Обратим внимание на точный синтаксис метода: ТаблицаЗначений.Найти(<Значение>, <Колонки>). Первым параметром всегда указывается искомое значение, а вторым — имя колонки.


Функция ЕстьВыбранныеТовары(ТаблицаТоваров)
    
    Возврат ТаблицаТоваров.Найти(Истина, "Выбран") <> Неопределено;
    
КонецФункции

Важное ограничение: метод Найти() существует у объекта ТаблицаЗначений, но отсутствует у объекта ТабличнаяЧасть. Кроме того, на клиенте в управляемой форме коллекция строк обычно представлена типом ДанныеФормыКоллекция, у которого такого метода также нет. Поэтому данный однострочник подходит строго для серверного кода и работы с чистыми таблицами значений.

Вариант 3. Использование метода «НайтиСтроки»

Нередко на практике используют платформенный метод отбора строк по структуре условий:


Функция ЕстьВыбранныеТовары(ТаблицаТоваров)
    
    ПараметрыОтбора = Новый Структура("Выбран", Истина);
    Возврат ТаблицаТоваров.НайтиСтроки(ПараметрыОтбора).Количество() > 0;
    
КонецФункции

Рассмотрим особенности этого метода:

  1. Метод НайтиСтроки() доступен как для ТаблицаЗначений, так и для ТабличнаяЧасть.
  2. В отличие от быстрого цикла или метода Найти(), метод НайтиСтроки() всегда просматривает коллекцию целиком и формирует массив найденных строк. Если в таблице тысячи позиций и большинство из них выбрано, создание промежуточного массива ради простой проверки факта наличия записей создаст неоправданный расход памяти и процессорного времени.

Распространенные заблуждения при оптимизации

Разберем типичные ошибки, которые нередко предлагают в качестве решений на профильных форумах:

  1. Подсчет итога по булевой колонке: попытка написать конструкцию вида ТаблицаТоваров.Итог("Выбран") > 0 вызовет исключение платформы. Метод Итог() предназначен исключительно для колонок числового типа. Применение его к колонкам с типом Булево недопустимо.
  2. Перепутанные параметры метода «Найти»: ошибочный вызов ТаблицаТоваров.Найти("Выбран", Истина) приведет к поиску текстовой строки "Выбран" во всей таблице, что не решит поставленную задачу. Корректный порядок: сначала значение (Истина), затем имя колонки ("Выбран").

Таким образом, для безопасного рефакторинга кода проверки пометки строк наилучшим выбором остается компактный цикл Для Каждого с ранним выходом через Возврат Истина, либо точечный метод Найти(Истина, "Выбран"), если обработка выполняется исключительно над объектом ТаблицаЗначений.

← На главную