Как заполнить массив структур в 1С и рассчитать данные по дням недели

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

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

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

Постановка задачи и декомпозиция

Давайте проанализируем требования исходной задачи. Нам необходимо реализовать две части:

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

Для реализации первой части нам потребуется организовать цикл по дням месяца. Для второй части — выполнить обход уже заполненного массива и применить алгоритмы ветвления.

Анализ типичных ошибок в коде новичка

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

Ошибка 1. Ручное изменение счетчика внутри цикла типа «Для»

В первой части своего решения автор написал следующую конструкцию:


Для Счетчик = Счетчик1 по чслКоличестовДней Цикл
    // ... логика ...
    Счетчик = Счетчик + 1;
КонецЦикла;

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

Ошибка 2. Локалезависимость и сравнение строк при определении дней недели

Для определения выходных дней автор использует следующее условие:


Если НашаСтруктура.ДеньНедели = "суббота" Или НашаСтруктура.ДеньНедели = "воскресенье" Тогда

Выясним причину, почему такой подход считается плохой практикой в профессиональной разработке. Функция Формат() с параметром ДФ=dddd возвращает строковое представление дня недели на языке текущей локализации операционной системы. Если база данных будет запущена на сервере с англоязычной операционной системой, функция вернет значения "Saturday" и "Sunday". В итоге ваше условие никогда не выполнится, а программа отработает некорректно. Также не стоит забывать про регистр букв: сравнение строки со значением с заглавной буквы (например, «Суббота») вернет ложь. Для определения выходных дней необходимо использовать числовые индексы дней недели.

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

Во второй части кода автор пытается обратиться к переменной НашаСтруктура, которая была объявлена в первой процедуре:

Если НашаСтруктура.ДеньНедели = "суббота" Тогда ...

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

Ошибка 4. Неявное сравнение разных типов данных

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

Если ЭлМассива = чслМясоВВыходные Тогда

Здесь ЭлМассива — это очередной элемент нашего массива, то есть объект типа Структура. Переменная чслМясоВВыходные — это число. Попытка сравнить сложный объект (структуру) с числом напрямую приведет к ошибке времени выполнения. Чтобы получить значение, необходимо обращаться к конкретному свойству структуры по его ключу: ЭлМассива.Количество.

Ошибка 5. Избыточное использование Таблицы Значений

Использование объекта ТаблицаЗначений для решения этой задачи (как показано в сообщении 29) является классическим примером избыточного усложнения кода. Таблица значений — это тяжелый универсальный объект, предназначенный для работы со сложными реляционными структурами данных в оперативной памяти. Для простого подсчета сумм и количества элементов в массиве из 30 элементов создавать таблицу значений неэффективно. Достаточно использовать простые переменные-аккумуляторы в обычном цикле обхода массива.

Часть 1: Создание и заполнение массива структур

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

Для этого напишем следующий программный код:


// Определим период работы — текущий месяц
ДатаМесяца = НачалоМесяца(ТекущаяДата());
КоличествоДнейВМесяце = День(КонецМесяца(ДатаМесяца));

// Инициализируем массив для хранения структур
МассивМороженого = Новый Массив;

// Зададим условные объемы потребления мороженого
КолвоВБудни = 2; // Например, 2 порции в будний день
КолвоВВыходные = 5; // Например, 5 порций в выходной день

Для ЧислоМесяца = 1 По КоличествоДнейВМесяце Цикл
    
    // Рассчитываем точную дату для текущего шага цикла
    ТекущаяДатаДня = ДатаМесяца + (ЧислоМесяца - 1) * 86400; // 86400 — количество секунд в сутках
    
    // Получаем номер дня недели (от 1 — понедельник, до 7 — воскресенье)
    НомерДняНедели = ДеньНедели(ТекущаяДатаДня);
    ЭтоВыходной = (НомерДняНедели = 6 Или НомерДняНедели = 7);
    
    // Определяем количество мороженого с помощью тернарного оператора
    КоличествоМороженого = ?(ЭтоВыходной, КолвоВВыходные, КолвоВБудни);
    
    // Получаем локализованное название дня недели исключительно для вывода на экран
    ИмяДняНедели = Формат(ТекущаяДатаДня, "ДФ=dddd");
    
    // Создаем структуру и сразу заполняем ее конструктором (это быстрее, чем метод Добавить)
    ДанныеДня = Новый Структура("ДеньНедели, ДеньМесяца, Количество", 
                                ИмяДняНедели, ЧислоМесяца, КоличествоМороженого);
    
    // Добавляем готовую структуру в массив
    МассивМороженого.Добавить(ДанныеДня);
    
КонецЦикла;

// Выводим результаты в окно сообщений
Сообщить("--- Данные по потреблению мороженого за месяц ---");
Для Каждого Элемент Из МассивМороженого Цикл
    // Используем современную функцию СтрШаблон для красивого форматирования строк
    СтрокаВывода = СтрШаблон("%1 %2: %3", Элемент.ДеньМесяца, Элемент.ДеньНедели, Элемент.Количество);
    Сообщить(СтрокаВывода);
КонецЦикла;

Разберем по шагам ключевые моменты этого алгоритма:

  1. Мы определили дату начала и количество дней в текущем месяце с помощью встроенных функций НачалоМесяца, КонецМесяца и День. Это позволяет коду автоматически адаптироваться под любой месяц года (28, 29, 30 или 31 день).
  2. Для вычисления даты конкретного дня мы прибавляем к началу месяца количество секунд: (ЧислоМесяца - 1) * 86400. Это самый быстрый и надежный способ смещения даты в 1С.
  3. Функция ДеньНедели() возвращает число от 1 до 7. Этот метод абсолютно надежен, так как он не зависит от языка интерфейса пользователя и операционной системы. Понедельник всегда будет равен 1, а воскресенье — 7.
  4. Вместо последовательного вызова метода Добавить() для структуры, мы использовали конструктор: Новый Структура("ИмяКолонки1, ИмяКолонки2", Значение1, Значение2). Такой подход работает быстрее и делает код более лаконичным.
  5. При выводе данных мы применили функцию СтрШаблон(). Она заменяет маркеры %1, %2 и т.д. на переданные значения. Это избавляет разработчика от долгой и некрасивой конкатенации строк через оператор «плюс» и автоматически преобразует числа в строку без лишних пробелов.

Часть 2: Обработка данных и подсчет результатов по кнопке

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

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


// Процедура-обработчик нажатия кнопки на форме
&НаКлиенте
Процедура РассчитатьПоказателиНажатие(Кнопка)
    
    // В реальном приложении массив должен храниться в реквизите формы или передаваться как параметр.
    // Предположим, что МассивМороженого — это переменная, доступная в контексте модуля.
    Если МассивМороженого.Количество() = 0 Тогда
        ПоказатьПредупреждение(, "Массив данных пуст! Сначала заполните массив.");
        Возврат;
    КонецЕсли;
    
    // Инициализируем переменные для подсчета статистики
    КоличествоБудней = 0;
    СуммаБудней = 0;
    
    КоличествоВыходных = 0;
    СуммаВыходных = 0;
    
    КоличествоСуббот = 0;
    
    // Обходим массив структур в цикле
    Для Каждого ЗаписьДня Из МассивМороженого Цикл
        
        // Для безопасного текстового сравнения приведем строку дня недели к нижнему регистру
        ДеньНеделиСтрокой = НРег(ЗаписьДня.ДеньНедели);
        
        // Проверяем, является ли день выходным
        Если ДеньНеделиСтрокой = "суббота" Или ДеньНеделиСтрокой = "воскресенье" Тогда
            
            КоличествоВыходных = КоличествоВыходных + 1;
            СуммаВыходные = СуммаВыходные + ЗаписьДня.Количество;
            
            // Если это суббота, увеличиваем отдельный счетчик суббот
            Если ДеньНеделиСтрокой = "суббота" Тогда
                КоличествоСуббот = КоличествоСуббот + 1;
            КонецЕсли;
            
        Иначе // Если день рабочий (будний)
            
            КоличествоБудней = КоличествоБудней + 1;
            СуммаБудней = СуммаБудней + ЗаписьДня.Количество;
            
        КонецЕсли;
        
    КонецЦикла;
    
    // Выводим полученные результаты пользователю
    Сообщить("=== Результаты статистического анализа ===");
    
    Сообщить(СтрШаблон("Будние дни: приемов мороженого — %1, съедено всего порций — %2", 
                       КоличествоБудней, СуммаБудней));
                       
    Сообщить(СтрШаблон("Выходные дни: приемов мороженого — %1, съедено всего порций — %2", 
                       КоличествоВыходных, СуммаВыходные));
                       
    Сообщить(СтрШаблон("Всего суббот в указанном периоде: %1", КоличествоСуббот));
    
КонецПроцедуры

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

Рекомендации по улучшению кода

Если вы хотите, чтобы ваш код соответствовал высшим стандартам разработки в среде «1С:Предприятие 8», обратите внимание на следующие рекомендации:

  1. Используйте типизацию: При создании переменных или реквизитов формы всегда указывайте их тип. В случае с массивом на форме, создайте реквизит формы с типом Произвольный, чтобы хранить коллекцию между серверными вызовами.
  2. Разделяйте интерфейс и логику: Старайтесь не писать тяжелые расчетные алгоритмы непосредственно в обработчиках кнопок на клиенте. Переносите расчетную логику в процедуры на сервере (или в серверные модули), возвращая на клиент только готовые структуры с результатами для отображения пользователю.
  3. Откажитесь от «магических чисел»: Вместо использования жестко заданного числа секунд в сутках (86400) в коде, хорошим тоном считается объявление константы или использование понятной переменной, например, СекундВСутках = 86400;. Это улучшает читаемость кода для других разработчиков.

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

← На главную