Как проверить заполнение ячеек таблицы значений без полного перебора

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

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

Вариант 1. Использование платформенного метода НайтиСтроки с отбором по структуре

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

Разберем по шагам, как настроить такой отбор:

  1. При создании динамических колонок формируем структуру отбора, где ключами выступают имена проверяемых колонок, а значениями — дефолтные нули.
  2. Сохраняем готовую структуру в переменной формы или контексте модуля, чтобы не пересоздавать ее каждый раз.
  3. Применяем метод НайтиСтроки() к готовой таблице.

Посмотрим на пример программной реализации:


// Формирование структуры отбора один раз (например, при создании колонок)
ОтборНезаполненных = Новый Структура;
Для Индекс = 4 По ТЗ.Колонки.Количество() - 1 Цикл
    ОтборНезаполненных.Вставить(ТЗ.Колонки[Индекс].Имя, 0);
КонецЦикла;

// Быстрый поиск строк, где во всех указанных колонках остались нули
ПустыеСтроки = ТЗ.НайтиСтроки(ОтборНезаполненных);

Ограничение метода: данный подход работает только на строгое равенство одному конкретному значению (например, 0). Если «незаполненной» ячейкой может считаться как 0, так и -1 или Неопределено, стандартный метод НайтиСтроки() применить уже не получится, так как он не поддерживает составные логические условия.

Вариант 2. Фиксация признака изменения строки в момент ввода данных

Чтобы не обходить таблицу после завершения ввода, можно фиксировать факт заполнения ячеек в момент интерактивного редактирования пользователем. Для этого в таблицу добавляется служебная булева колонка Изменена или Заполнена.

Однако здесь программисты часто сталкиваются с ошибкой позиционирования. Проанализируем типичную ситуацию: разработчик берет свойство Элемент.ТекущаяСтрока и пытается обратиться по нему к коллекции через квадратные скобки:


// ОШИБОЧНЫЙ КОД:
НомерСтроки = Элемент.ТекущаяСтрока;
ЭтаФорма.РаспределениеСуммПоСчетам[НомерСтроки].Изменена = Истина;

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

Правильный способ работы с текущей строкой на клиенте без серверных вызовов выглядит следующим образом:


&НаКлиенте
Процедура ТаблицаЧиселПриОкончанииРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования)
    Если ОтменаРедактирования Тогда
        Возврат;
    КонецЕсли;
    
    // Прямой доступ к текущим данным редактируемой строки
    ТекущиеДанные = Элемент.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        ТекущиеДанные.Заполнена = Истина;
    КонецЕсли;
КонецПроцедуры

Если требуется получить строку коллекции формы по известному идентификатору, следует использовать метод ДанныеФормыКоллекция.НайтиПоИдентификатору(Идентификатор).

Вариант 3. Оптимизированный перебор строк и колонок

Когда таблица требует сложного анализа (проверка на несколько значений: 0, -1, пустые ссылки, параллельное преобразование данных в структуры), без циклов обойтись невозможно. Однако двойной цикл можно существенно ускорить, избегая типичных узких мест.

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

  1. Логическая ошибка при проверке исключений: условие вида Значение <> 0 ИЛИ Значение <> -1 является грубой ошибкой, так как оно всегда истинно для любого числа (ноль не равен минус единице, а минус единица не равна нулю). Корректным является только условие с оператором «И»: Значение <> 0 И Значение <> -1.
  2. Повторный анализ имен колонок в цикле: строковые операции вида СтрНачинаетсяС(Колонка.Имя, ...) и обращение к ячейке по имени Строка[Колонка.Имя] на каждой итерации вызывают тяжелый поиск по хэш-таблицам. Колонки по типам и индексам необходимо классифицировать до входа в цикл обработки строк.

Посмотрим на оптимизированный пример обхода широкой таблицы значений:


// 1. Предварительная подготовка списков целевых колонок ДО цикла
МассивКолонокСумм = Новый Массив;
Для Каждого Колонка Из ТаблицаДанных.Колонки Цикл
    Если СтрНачинаетсяС(Колонка.Имя, "СуммаПеревода") 
       Или СтрНачинаетсяС(Колонка.Имя, "СуммаИнкассации") Тогда
        // Сохраняем имя или индекс колонки
        МассивКолонокСумм.Добавить(Колонка.Имя);
    КонецЕсли;
КонецЦикла;

// 2. Основной цикл обработки строк
Для Каждого СтрДанных Из ТаблицаДанных Цикл
    СтрокаЗаполнена = Ложь;
    
    // Проверяем только целевые колонки
    Для Каждого ИмяКолонки Из МассивКолонокСумм Цикл
        ТекущееЗначение = СтрДанных[ИмяКолонки];
        
        // Исправленное условие: значение не равно 0 И не равно -1
        Если ТекущееЗначение <> 0 И ТекущееЗначение <> -1 Тогда
            СтрокаЗаполнена = Истина;
            Прервать; // Выходим из цикла по колонкам сразу при первом совпадении
        КонецЕсли;
    КонецЦикла;
    
    Если СтрокаЗаполнена Тогда
        // Выполняем полезную обработку заполненной строки
        ОбработатьЗаполненнуюСтроку(СтрДанных);
    КонецЕсли;
КонецЦикла;

Выводы и рекомендации

Выбор оптимального механизма зависит от структуры бизнес-логики:

  1. Если критерий незаполненности ячеек строго однороден (только 0) — используем НайтиСтроки() по предварительно скомпонованной структуре отбора.
  2. Если заполнение отслеживается интерактивно — используем обработчики событий элементов управления и свойство ТекущиеДанные (помня, что ТекущаяСтрока возвращает идентификатор, а не номер строки).
  3. При комплексном анализе значений используем цикл, вынося все строковые проверки метаданных колонок за пределы итераций по строкам и применяя оператор Прервать при первом обнаружении значащего числа.
← На главную