Как найти строки в таблице значений в 1С 7.7: реализация аналога НайтиСтроки

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Способ 1. Сортировка и выборка диапазона (наиболее эффективный штатный метод)

Если нам необходимо найти все строки с определенным значением в колонке, самым быстрым способом на чистом встроенном языке будет предварительная сортировка таблицы. Разберем логику: после сортировки все строки с одинаковыми значениями выстраиваются друг за другом. Нам остается найти первую такую строку и собрать последующие, пока значение не изменится.

Проанализируем программную реализацию этого метода:


Функция глОтобратьПоКолонке(ТЗВход, Колонка, Значение) Экспорт
    Перем ТЗ, ТЗВрем, НомСтр, НомСтр2, ТЗКС, счСтрок;
    
    // Создаем копию таблицы, чтобы не испортить оригинал сортировкой
    ТЗВход.Выгрузить(ТЗ);
    
    // Сортируем по нужной колонке. Символ "*" означает сортировку по внутреннему значению
    ТЗ.Сортировать(Колонка + "*");
    
    НомСтр = 0;
    // Ищем первое вхождение значения
    Если ТЗ.НайтиЗначение(Значение, НомСтр, Колонка) = 0 Тогда 
        ТЗ.УдалитьСтроки(); 
        Возврат ТЗ; 
    КонецЕсли; 
    
    ТЗКС = ТЗ.КоличествоСтрок();
    НомСтр2 = ТЗКС;
    
    // Проверяем, является ли последняя строка искомой
    Если ТЗ.ПолучитьЗначение(ТЗКС, Колонка) <> Значение Тогда
        // Если нет, ищем границу диапазона перебором
        Для счСтрок = НомСтр + 1 По ТЗКС - 1 Цикл
            Если ТЗ.ПолучитьЗначение(счСтрок, Колонка) <> Значение Тогда
                НомСтр2 = счСтрок - 1;
                Прервать;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
        
    ТЗВрем = СоздатьОбъект("ТаблицаЗначений");
    // Выгружаем только найденный диапазон строк в новую таблицу
    ТЗ.Выгрузить(ТЗВрем, НомСтр, НомСтр2);
    
    Возврат ТЗВрем;
КонецФункции

Этот подход работает значительно быстрее обычного полного перебора через ПолучитьЗначение, особенно на больших массивах данных, так как мы минимизируем количество обращений к строкам таблицы в цикле.

Способ 2. Использование компоненты 1С++ и Индексированной таблицы

Если производительность является критическим фактором или поиск требуется выполнять очень часто, мы рекомендуем использовать внешнюю компоненту 1С++. Она добавляет объект ИндексированнаяТаблица, который является практически полным аналогом таблицы значений из 1С 8.х и обладает встроенным методом поиска.

Рассмотрим преимущества этого метода:

  1. Высокая скорость: Индексы строятся в оперативной памяти и позволяют находить строки мгновенно.
  2. Наличие метода НайтиСтроки: Вы можете использовать синтаксис, максимально приближенный к 8-й версии.
  3. Фильтрация: Метод УстановитьФильтр позволяет скрыть ненужные строки, оставив для обработки только подходящие.

Пример использования метода УстановитьФильтр в 1С++:


Твр = СоздатьОбъект("ИндексированнаяТаблица");
// ... заполнение таблицы ...
// Устанавливаем фильтр: показывать только строки, где в колонке "Клиент" заданное значение
Твр.УстановитьФильтр(ВыбКлиент, ВыбКлиент, "Клиент");
// Теперь при обходе таблицы (ВыбратьСтроки / ПолучитьСтроку) будут видны только отобранные строки

Способ 3. Бинарный поиск (дихотомия)

В ситуациях, когда таблица уже отсортирована, но мы не хотим копировать данные в новую таблицу, можно применить алгоритм бинарного поиска. Это классический метод программирования, который позволяет найти позицию элемента за минимальное количество шагов.

Разберем пример функции, реализующей такой поиск по ключу (в качестве ключа используется СписокЗначений, где представления элементов — это имена колонок):


Функция ТЗ_НайтиПоКлючу2(ТЗ, Ключ, ПоВнутрПредставлению = 0, НачСтрока = 0, КонСтрока = 0, ИскатьПоследнюю = 0) Экспорт
    Перем н1, н, н2, Рез, Найдено;
    
    н1 = ?(НачСтрока = 0, 1, НачСтрока);
    н2 = ?(КонСтрока = 0, ТЗ.КоличествоСтрок(), КонСтрока);
    Найдено = 0;
    
    Пока н1 < н2 Цикл
        н = Цел((н1 + н2) / 2);
        Если ИскатьПоследнюю = 1 Тогда
            н = мин(н + 1, н2);
        КонецЕсли;
        
        Рез = СравнитьСКлючом(Ключ, ТЗ, н, ПоВнутрПредставлению);
        Если Рез = 0 Тогда
            Если ИскатьПоследнюю = 0 Тогда
                н2 = н;
            Иначе
                н1 = н;
            КонецЕсли;
            Найдено = 1;
        ИначеЕсли Рез < 0 Тогда
            н2 = н - 1;
        Иначе
            н1 = н + 1;
        КонецЕсли;
    КонецЦикла;
    
    Возврат ?(Найдено = 1, н1, 0);
КонецФункции

Важный нюанс: Бинарный поиск эффективен для нахождения первой и последней границы диапазона. Зная эти номера строк, вы сможете обработать все промежуточные строки обычным циклом Для.

Способ 4. Оптимизированный перебор через НайтиЗначение

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

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


Функция ВыборкаИзТаблицы(перТаблица, перКолонка, перЗначение)
    Результат = СоздатьОбъект("ТаблицаЗначений");
    // Копируем структуру колонок
    перТаблица.Выгрузить(Результат, 1, 1);
    Результат.УдалитьСтроки();
    
    НомСтроки = 0;
    // Пока находим значение, продолжаем поиск со следующей строки
    Пока перТаблица.НайтиЗначение(перЗначение, НомСтроки, перКолонка) > 0 Цикл
        Результат.НоваяСтрока();
        // Копируем данные по всем колонкам
        Для Счетчик = 1 По перТаблица.КоличествоКолонок() Цикл
            текИмяКолонки = перТаблица.ТекущаяКолонка(Счетчик);
            Если ПустоеЗначение(текИмяКолонки) = 0 Тогда
                Результат.УстановитьЗначение(Результат.НомерСтроки, текИмяКолонки, перТаблица.ПолучитьЗначение(НомСтроки, текИмяКолонки));
            КонецЕсли;
        КонецЦикла;
        // Чтобы НайтиЗначение не зациклилось на той же строке,
        // в следующей итерации поиск начнется автоматически с НомСтроки + 1,
        // если передать переменную обратно в метод.
    КонецЦикла;
    Возврат Результат;
КонецФункции

Практические советы по оптимизации

Проанализируем несколько важных правил, которые помогут ускорить работу с таблицами значений в 1С 7.7:

  1. Избегайте удаления строк в цикле: Удаление строки из середины большой таблицы (через УдалитьСтроку) — крайне медленная операция, так как платформа физически сдвигает все последующие строки в памяти. Всегда лучше создать новую таблицу и скопировать в нее нужные данные.
  2. Типизация колонок: При создании таблицы всегда указывайте типы данных в методе НоваяКолонка. Поиск по типизированным колонкам происходит быстрее, так как системе не нужно определять тип каждого значения "на лету".
  3. Использование составных ключей: Если вам нужно искать строки по совпадению сразу в нескольких колонках, создайте одну служебную колонку "Ключ" и записывайте туда склеенные значения (например, Товар + "|" + Склад). Поиск по одной колонке всегда эффективнее вложенных циклов.
  4. Scripting.Dictionary: Для мгновенного поиска уникальных элементов можно использовать COM-объект Scripting.Dictionary. В нем можно хранить пары "Значение-Ключ" и "Номер строки", что обеспечивает практически мгновенный доступ к данным без перебора таблицы.

Подводя итог, можно сказать: несмотря на отсутствие прямого аналога НайтиСтроки, платформа 1С 7.7 предоставляет достаточно инструментов для эффективной работы. Для простых задач подойдет сортировка + выгрузка диапазона, а для сложных и нагруженных систем лучшим выбором станет использование 1С++.

← На главную