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