Поиск наибольшего элемента в массиве, таблице значений и запросе на встроенном языке 1С
Содержание
Чтобы найти максимальное значение в массиве 1С, пройдите массив одним циклом и сравнивайте каждый элемент с текущим максимумом: Максимум = Макс(Максимум, Элемент). Метода вроде Массив.Максимум() в платформе нет, и функции «МинМакс» тоже нет: есть только встроенные Макс() и Мин(), которые принимают список значений, а не массив.
Способ выбирают по тому, где лежат данные. Массив в памяти — перебор. Таблица значений — выгрузка колонки и тот же перебор либо сортировка. Данные в базе — запрос с агрегатной функцией МАКСИМУМ, без загрузки записей в память.
Функции Макс и Мин: что они умеют и чего не умеют
Макс() и Мин() — глобальные функции встроенного языка. Они принимают два и больше параметров через запятую и возвращают наибольший или наименьший из них.
Результат = Макс(15, 42, 7); // 42
Наименьшее = Мин(15, 42, 7); // 7
ПозднееДата = Макс('20250301', '20250115'); // 01.03.2025
Ограничения, о которые спотыкаются чаще всего:
- 🚫 Массив целиком передать нельзя. Функция ждёт отдельные значения через запятую и не «раскрывает» коллекцию, поэтому
Макс(МойМассив)наибольший элемент не найдёт. - 🚫 Все параметры должны быть одного примитивного типа. Число со строкой, дату с
Неопределеносравнить нельзя — будет ошибка выполнения. - ✅ Зато
Макс()удобно вызывать внутри цикла: она заменяет конструкциюЕсли … Тогдав одну строку.
Макс(0, Остаток) — короткий способ «обрезать» отрицательное значение до нуля. Пригодится, когда остаток не должен уходить в минус в расчётах.
Ищем максимум в массиве перебором
Перебор — основной способ. Он проходит массив один раз и не меняет порядок элементов.
Короткий вариант через Макс
Числа = Новый Массив;
Числа.Добавить(120);
Числа.Добавить(305);
Числа.Добавить(87);
Если Числа.Количество() > 0 Тогда
Максимум = Числа[0];
Для Каждого Элемент Из Числа Цикл
Максимум = Макс(Максимум, Элемент);
КонецЦикла;
Сообщить("Максимум: " + Формат(Максимум, "ЧГ=0")); // Максимум: 305
КонецЕсли;
Стартовое значение берите из первого элемента, а не из нуля. Если все числа отрицательные (−5, −12, −3), старт с нуля вернёт 0, которого в массиве нет. Правильный ответ здесь −3.
Максимум вместе с индексом элемента
Часто нужно не только число, но и позиция: чтобы потом взять элемент из параллельного массива или строку таблицы. Тогда Макс() не подходит, нужен явный индекс.
// Возвращает наибольший элемент массива или Неопределено для пустого массива.
// В параметр ИндексМакс записывает позицию найденного элемента.
Функция МаксимумМассива(Массив, ИндексМакс = Неопределено) Экспорт
ИндексМакс = Неопределено;
Если Массив.Количество() = 0 Тогда
Возврат Неопределено;
КонецЕсли;
Результат = Массив[0];
ИндексМакс = 0;
Для Индекс = 1 По Массив.ВГраница() Цикл
Если Массив[Индекс] > Результат Тогда
Результат = Массив[Индекс];
ИндексМакс = Индекс;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Вызов:
Позиция = Неопределено;
Наибольшее = МаксимумМассива(Числа, Позиция);
// Для массива 120, 305, 87: Наибольшее = 305, Позиция = 1
Как работает функция:
- Пустой массив сразу возвращает
Неопределено— вызывающий код должен это проверить. - Первый элемент становится кандидатом, индекс 0.
- Цикл идёт с индекса 1 до
ВГраница(). Для массива из одного элемента циклДля Индекс = 1 По 0не выполнится ни разу — это нормально, ответ уже вРезультат. - Строгое сравнение
>оставляет индекс первого из равных максимумов. Нужен последний — замените на>=.
Функцию удобно положить в общий модуль с флажками «Сервер» и «Клиент»: массив доступен в обоих контекстах.
Если индекс нужен после того, как максимум уже найден через Макс(), есть метод Числа.Найти(Максимум). Он вернёт индекс первого совпадения. Но это второй проход по массиву, на больших объёмах лучше сразу считать индекс в цикле.
Минимум и вложенные массивы
Минимум ищется зеркально: Мин() вместо Макс() и < вместо >.
Для массива массивов (например, строки × столбцы) достаточно двух вложенных циклов Для Каждого и одной переменной-кандидата. Инициализируйте её первым элементом первого непустого вложенного массива, а не нулём.
Максимальное значение в таблице значений
У таблицы значений есть метод Итог("Колонка"), но он считает сумму, а не максимум. Готового метода для наибольшего значения нет. Рабочих способов три.
Способ 1. Выгрузить колонку и перебрать
Суммы = ТЗ.ВыгрузитьКолонку("Сумма"); // обычный массив
ИндексМакс = Неопределено;
МаксСумма = МаксимумМассива(Суммы, ИндексМакс);
Если ИндексМакс <> Неопределено Тогда
СтрокаМакс = ТЗ[ИндексМакс]; // строка таблицы целиком
КонецЕсли;
ВыгрузитьКолонку() сохраняет порядок строк, поэтому индекс в массиве совпадает с индексом строки. Таблица остаётся нетронутой.
Способ 2. Отсортировать по убыванию
Копия = ТЗ.Скопировать();
Копия.Сортировать("Сумма Убыв");
Если Копия.Количество() > 0 Тогда
СтрокаМакс = Копия[0];
КонецЕсли;
Сортировка меняет порядок строк исходной таблицы. Если таблица потом выводится на форму или в печатную форму, сортируйте копию. Плюс способа — можно отсортировать по двум колонкам: "Сумма Убыв, Дата Убыв" выберет из равных сумм самую позднюю.
Способ 3. Все строки с максимальным значением
Если одинаковый максимум может быть у нескольких строк, найдите число любым способом, а затем отберите строки:
Отбор = Новый Структура("Сумма", МаксСумма);
СтрокиМакс = ТЗ.НайтиСтроки(Отбор); // массив строк
Для таблиц на десятки тысяч строк заранее добавьте индекс: ТЗ.Индексы.Добавить("Сумма") — поиск по колонке станет быстрее.
Таблица значений недоступна на тонком клиенте. Код с ней пишите в серверных процедурах или в общем модуле с флажком «Сервер».
Запрос с МАКСИМУМ: выборка наибольшего значения из базы
Если данные лежат в справочниках, документах или регистрах, не выгружайте их в массив ради одного числа. Агрегатная функция МАКСИМУМ посчитает результат на стороне СУБД.
Одно число
Пример для 1С:Бухгалтерии 3.0 — наибольшая сумма проведённой реализации:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(МАКСИМУМ(Реализация.СуммаДокумента), 0) КАК МаксСумма
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Проведен";
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
МаксСумма = Выборка.МаксСумма;
КонецЕсли;
Без ЕСТЬNULL на пустой выборке придёт NULL, и сравнение или арифметика с ним дадут ошибку. Записи с NULL в поле МАКСИМУМ пропускает сам.
Максимум по группам
"ВЫБРАТЬ
| Реализация.Контрагент КАК Контрагент,
| МАКСИМУМ(Реализация.СуммаДокумента) КАК МаксСумма
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Проведен
|СГРУППИРОВАТЬ ПО
| Реализация.Контрагент"
Получите крупнейшую отгрузку по каждому покупателю одним запросом.
Вся запись, а не только число
МАКСИМУМ возвращает значение поля, но не ссылку на документ. Чтобы получить сам документ, используйте сортировку и ПЕРВЫЕ 1:
"ВЫБРАТЬ ПЕРВЫЕ 1
| Реализация.Ссылка КАК Документ,
| Реализация.СуммаДокумента КАК Сумма
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Проведен
|УПОРЯДОЧИТЬ ПО
| Реализация.СуммаДокумента УБЫВ"
Максимум таблицы значений через запрос
Таблицу значений можно передать в запрос параметром. Источником &Таблица разрешено делать только запрос, который помещает данные во временную таблицу:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Сумма КАК Сумма
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &Таблица КАК Т
|;
|
|ВЫБРАТЬ
| ЕСТЬNULL(МАКСИМУМ(ВТ_Данные.Сумма), 0) КАК МаксСумма
|ИЗ
| ВТ_Данные КАК ВТ_Данные";
Запрос.УстановитьПараметр("Таблица", ТЗ);
Колонки такой таблицы должны быть типизированы: ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число")). Без описания типов запрос не выполнится. Ради одного максимума этот путь избыточен, но он оправдан, когда таблицу всё равно нужно соединить с данными базы.
Даты, строки и смешанные типы в массиве
Операции «больше» и «меньше» в 1С работают только для значений одного примитивного типа: Число, Строка, Дата, Булево. Отсюда правила для нечисловых массивов.
| Тип элементов | Что считается максимумом | На что смотреть |
|---|---|---|
| Дата | самая поздняя дата | пустая дата '00010101' всегда наименьшая |
| Строка | последняя по алфавиту | «10» меньше «9»: строки сравниваются посимвольно |
| Ссылка (справочник, документ) | сравнить напрямую нельзя | сравнивайте реквизит: дату, код, сумму |
| Смешанный массив | ошибка выполнения | фильтруйте по ТипЗнч() |
Числа, сохранённые строками
Типичный случай — значения пришли из файла или HTTP-ответа. Массив «9», «10», «105» как строки даст максимум «9». Перед поиском приведите элементы к числу:
Попытка
Значение = Число(СтрокаИзФайла);
Исключение
Значение = Неопределено; // не число — пропустить или записать в журнал
КонецПопытки;
Самая длинная строка
Если нужен не «алфавитный» максимум, а строка наибольшей длины, сравнивайте СтрДлина(Элемент), а в кандидате храните саму строку.
Массив со смешанными типами
Пропускайте всё, что не число, и стартуйте с Неопределено:
Максимум = Неопределено;
Для Каждого Элемент Из Массив Цикл
Если ТипЗнч(Элемент) <> Тип("Число") Тогда
Продолжить;
КонецЕсли;
Если Максимум = Неопределено Или Элемент > Максимум Тогда
Максимум = Элемент;
КонецЕсли;
КонецЦикла;
Условие с Или вычисляется слева направо и останавливается на первой истине, поэтому на первом числе сравнение с Неопределено не выполняется.
Максимум среди ссылок
Документы удобнее сравнивать по дате или сумме. Если массив ссылок уже в памяти, получите реквизиты одним запросом с условием Ссылка В (&МассивСсылок) и УПОРЯДОЧИТЬ ПО ... УБЫВ — это дешевле, чем обращаться к Элемент.Дата в цикле: каждое такое обращение читает объект из базы.
Какой способ поиска максимума выбрать
| Способ | Когда подходит | Минусы |
|---|---|---|
Перебор с Макс() | массив в памяти, нужно только число | индекс не возвращает |
| Перебор с индексом | нужна позиция или строка таблицы | на пару строк кода больше |
СписокЗначений + СортироватьПоЗначению | нужен сразу весь порядок, а не только максимум | копирование и сортировка медленнее одного прохода |
ТЗ.Сортировать("… Убыв") | нужна строка, есть второй критерий при равенстве | меняет порядок строк исходной таблицы |
Запрос с МАКСИМУМ | данные в базе | нужен текст запроса и проверка NULL |
ПЕРВЫЕ 1 … УБЫВ | нужен документ или элемент целиком | — |
Для массива сортировка выглядит так:
Список = Новый СписокЗначений;
Список.ЗагрузитьЗначения(Массив);
Список.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
Максимум = Список[0].Значение; // при непустом массиве
Собственного метода сортировки у Массив нет, поэтому пользуются этим приёмом. Но ради одного максимума он проигрывает перебору: один проход по 100 000 элементам всегда дешевле, чем копирование и сортировка того же объёма.
Ошибки при поиске максимального значения
- ❌ Старт с нуля. Для массива отрицательных чисел вернёт 0. Берите первый элемент.
- ❌ Пустой массив без проверки. Обращение
Массив[0]приКоличество() = 0вызывает ошибку «Индекс находится за границами диапазона». Проверяйте количество до цикла. - ❌
Итог()вместо максимума. У таблицы значений этот метод суммирует колонку. - ❌ Удаление элементов в прямом цикле. Если по ходу поиска вы чистите массив от мусора, идите с конца:
Для Сч = 1 По Массив.Количество() Цикл Индекс = Массив.Количество() - Сч; …. Прямой проход после удаления пропускает соседний элемент. - ❌ Поиск в цикле по запросу на каждую строку. Сначала соберите данные одним запросом, потом ищите максимум в памяти или сразу через
МАКСИМУМ. - ❌ Строки неограниченной длины в запросе. Для поля типа «Строка» без ограничения длины агрегат и сортировка недоступны. Приведите поле:
ВЫРАЗИТЬ(Т.Комментарий КАК СТРОКА(100)). - ❌ Склейка числа со строкой при выводе.
"Максимум: " + Максимумдля числа 1500 даст «1 500» с неразрывным пробелом. Для логов и файлов используйтеФормат(Максимум, "ЧГ=0").