Как найти в таблице значений строку с наиболее близким значением к заданному числу

Программист 1С v8.3 (Обычные формы)
← На главную

В практике разработки на платформе 1С:Предприятие часто возникает задача поиска данных, которые не в точности соответствуют условию, а являются максимально приближенными к нему. Например, при анализе рентабельности нам может потребоваться найти строку, значение которой наиболее близко к 100%. Прямой поиск методом Найти() здесь не поможет, так как он работает только на точное совпадение. В этой статье мы подробно разберем несколько эффективных способов решения этой задачи.

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

Проанализируем самый простой и интуитивно понятный метод. Он заключается в создании временной колонки, в которой будет храниться разница (отклонение) между текущим значением и целевым числом (в нашем случае — 100). Важно вычислять именно абсолютное значение (модуль) разницы, чтобы числа 98 и 102 считались одинаково удаленными от 100.

Рассмотрим пошаговый алгоритм реализации этого метода:

  1. Добавляем в таблицу значений новую колонку (например, Отклонение) с типом Число.
  2. В цикле обходим все строки таблицы и записываем в новую колонку результат вычисления модуля разницы.
  3. Сортируем таблицу по колонке Отклонение по возрастанию.
  4. Первая строка таблицы после сортировки и будет искомой.

Посмотрим на пример кода, реализующий этот подход:


// Предположим, ТЗ — наша исходная ТаблицаЗначений с колонкой "Рентабельность"
ТЗ.Колонки.Добавить("Отклонение", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));

ЦелевоеЗначение = 100;

Для Каждого СтрокаТЗ Из ТЗ Цикл
    // Вычисляем модуль разницы: |Значение - 100|
    СтрокаТЗ.Отклонение = Макс(СтрокаТЗ.Рентабельность - ЦелевоеЗначение, ЦелевоеЗначение - СтрокаТЗ.Рентабельность);
КонецЦикла;

// Сортируем по возрастанию отклонения
ТЗ.Сортировать("Отклонение Возр");

// Первая строка содержит самое близкое значение
Если ТЗ.Количество() > 0 Тогда
    БлижайшаяСтрока = ТЗ[0];
    Сообщить("Ближайшее значение: " + БлижайшаяСтрока.Рентабельность);
КонецЕсли;

Способ 2. Поиск в одном цикле без изменения структуры таблицы

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

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


ЦелевоеЗначение = 100;
МинимальноеОтклонение = Неопределено;
БлижайшаяСтрока = Неопределено;

Для Каждого ТекущаяСтрока Из ТЗ Цикл
    ТекущееЗначение = ТекущаяСтрока.Рентабельность;
    
    // Вычисляем абсолютное отклонение вручную
    ТекущееОтклонение = ?(ТекущееЗначение > ЦелевоеЗначение, ТекущееЗначение - ЦелевоеЗначение, ЦелевоеЗначение - ТекущееЗначение);
    
    Если МинимальноеОтклонение = Неопределено Или ТекущееОтклонение < МинимальноеОтклонение Тогда
        МинимальноеОтклонение = ТекущееОтклонение;
        БлижайшаяСтрока = ТекущаяСтрока;
    КонецЕсли;
КонецЦикла;

Если БлижайшаяСтрока <> Неопределено Тогда
    // Мы нашли нужную строку
КонецЕсли;

Способ 3. Использование механизма запросов (универсальный подход)

Использование запросов считается "хорошим тоном" в 1С, особенно если данных много. Даже если данные находятся в ТаблицеЗначений, мы можем поместить её во временную таблицу и выполнить поиск средствами SQL. Это позволит избежать ручного перебора строк.

Разберем, как вычислить модуль числа в запросе, так как встроенной функции ABS() в языке запросов 1С нет. Мы используем конструкцию ВЫБОР:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ ПЕРВЫЕ 1
    |   Т.Рентабельность,
    |   ВЫБОР
    |       КОГДА Т.Рентабельность > 100 ТОГДА Т.Рентабельность - 100
    |       ИНАЧЕ 100 - Т.Рентабельность
    |   КОНЕЦ КАК Отклонение
    |ИЗ
    |   ВТ_Данные КАК Т
    |УПОРЯДОЧИТЬ ПО
    |   Отклонение ВОЗР";

Запрос.УстановитьПараметр("ТЗ", ВашаТаблицаЗначений);
РезультатЗапроса = Запрос.Выполнить();

Если Не РезультатЗапроса.Пустой() Тогда
    Выборка = РезультатЗапроса.Выбрать();
    Выборка.Следующий();
    // Искомое значение в Выборка.Рентабельность
КонецЕсли;

Нюансы и оптимизация процесса поиска

При решении данной задачи важно учитывать несколько критических моментов, которые могут повлиять на корректность результата:

Индексация для ускорения работы

Если поиск ближайшего значения выполняется многократно в коде (например, внутри другого цикла), рекомендуется использовать индексацию. Хотя индекс в 1С помогает при поиске на равенство, он также значительно ускоряет операцию Сортировать(). Для этого используется метод: ТЗ.Индексы.Добавить("Рентабельность");.

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

← На главную