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

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

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

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

Почему в «Соответствии» нет и не может быть порядка?

Для того чтобы понять суть проблемы, нам необходимо рассмотреть внутреннюю архитектуру платформы. Коллекция Соответствие в 1С реализована на базе структуры данных «хэш-таблица» (Hash Map). Это накладывает строгие технические ограничения на работу с элементами:

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

Таким образом, понятия «первый элемент» и «последний элемент» для коллекции Соответствие физически отсутствуют. Любое совпадение порядка при тестировании на малом объеме данных (например, когда у автора темы на 4 элементах «всё заработало») является случайным и нестабильным.

Разбор ошибок: почему не работает обращение по индексу?

Посмотрим на типичную ошибку, которую часто пытаются применить на практике:

ПослСтрока = МоёСоответствие.Получить(МоёСоответствие.Количество() - 1);

Проанализируем, почему этот код ошибочен. Метод Получить() у объекта Соответствие в качестве параметра принимает исключительно Ключ элемента, а не его порядковый номер или числовой индекс. Платформа попытается найти в коллекции элемент, у которого ключ равен числу Количество() - 1. Если такого ключа нет (например, вашими ключами являются строки или объекты метаданных), метод вернет значение Неопределено.

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

Решение 1. Сортировка ключей для гарантированного получения последнего значения

Если вам все же необходимо получить логически «последний» элемент (например, если ключами являются даты, числа или упорядоченные строки), мы можем применить алгоритм с промежуточной сортировкой.

Разберем этот процесс по шагам:

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

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


// Создаем список для хранения ключей
СписокКлючей = Новый СписокЗначений;

// Заполняем список ключами из нашего соответствия ОКВЭД
Для Каждого Элемент Из ОКВЭД Цикл
    СписокКлючей.Добавить(Элемент.Ключ);
КонецЦикла;

// Выполняем сортировку ключей по возрастанию
СписокКлючей.СортироватьПоЗначению(НаправлениеСортировки.Возр);

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

Решение 2. Как правильно прочитать JSON с сохранением порядка

Очень часто соответствие заполняется автоматически при десериализации данных из формата JSON (например, при интеграции с веб-сайтом). По умолчанию стандартная функция ПрочитатьJSON(ЧтениеJSON, Истина) преобразует объекты JSON в Соответствие, полностью уничтожая исходный порядок следования полей. Если порядок важен, мы можем решить эту проблему двумя путями.

Вариант А. Изменение структуры JSON на источнике (рекомендуемый подход)

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

Вместо неупорядоченной структуры типа:


{
  "Параметр1": "Значение1",
  "Параметр2": "Значение2"
}

Используйте массив объектов:


[
  {"Код": "Параметр1", "Значение": "Значение1"},
  {"Код": "Параметр2", "Значение": "Значение2"}
]

В коде 1С работа с такой структурой становится максимально простой, надежной и быстрой:


ДанныеМассива = ПрочитатьJSON(ЧтениеJSON); // На выходе получаем Массив структур
Если ДанныеМассива.Количество() > 0 Тогда
    // Гарантированно получаем самый последний элемент по индексу
    ПоследнийЭлемент = ДанныеМассива[ДанныеМассива.Количество() - 1];
    Сообщить("Ключ: " + ПоследнийЭлемент.Код + ", Значение: " + ПоследнийЭлемент.Значение);
КонецЕсли;

Вариант Б. Потоковое чтение JSON (когда формат нельзя изменить)

Если сторонний сайт присылает данные в жестком формате и переделать его нельзя, мы можем использовать последовательное потоковое чтение файла с помощью объекта ЧтениеJSON. При таком подходе платформа считывает токены один за другим ровно в том порядке, в каком они записаны в текстовом файле. Мы можем вручную переложить их в упорядоченную коллекцию.

Рассмотрим пример кода:


// Создаем ТаблицуЗначений для сохранения порядка
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Ключ");
ТаблицаДанных.Колонки.Добавить("Значение");

ПутьКФайлу = "C:\temp\response.json";
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(ПутьКФайлу);

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

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

Подведем итоги

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

← На главную