В процессе разработки на платформе 1С:Предприятие 7.7 часто возникает задача собрать уникальный список значений из определенной колонки табличной части документа. Например, если в документе 50 строк, но товары в них относятся всего к трем разным складам, нам нужно получить строку, содержащую только эти три номера склада без повторений. Рассмотрим подробнее несколько эффективных способов реализации этой задачи, проанализируем их плюсы и минусы, а также разберем готовые примеры кода.
Этот способ считается наиболее быстрым и лаконичным. Суть его заключается в том, чтобы выгрузить нужную колонку табличной части во временную таблицу значений, а затем воспользоваться встроенным методом Свернуть, который автоматически удалит все дублирующиеся строки. Проанализируем реализацию этого метода по шагам:
ТаблицаЗначений.Свернуть по этой колонке.Разберем пример кода, реализующий этот алгоритм:
// Создаем временную таблицу для обработки данных
ТЗ = СоздатьОбъект("ТаблицаЗначений");
// Выгружаем колонку "НомерСклада" из ТЧ текущего документа
// Предположим, что контекст документа доступен через ВыбДок
ВыбДок.ВыгрузитьТабличнуюЧасть(ТЗ, "НомерСклада");
// Группируем по колонке, чтобы оставить только уникальные значения
// Второй параметр пустой, так как нам не нужно ничего суммировать
ТЗ.Свернуть("НомерСклада", "");
// Теперь преобразуем результат в строку с разделителями
СЗ = СоздатьОбъект("СписокЗначений");
ТЗ.Выгрузить(СЗ, , , "НомерСклада");
РезультатСтрокой = СЗ.ВСтрокуСРазделителями();
// Важный нюанс: ВСтрокуСРазделителями() добавляет лишние кавычки
// Очистим результат от них для красоты
РезультатСтрокой = СтрЗаменить(РезультатСтрокой, """", "");
Сообщить("Уникальные склады: " + РезультатСтрокой);
Этот метод хорош тем, что он использует оптимизированные внутренние механизмы платформы 1С 7.7 для работы с коллекциями данных, что особенно заметно на документах с большим количеством строк.
Если по каким-то причинам вы не хотите создавать промежуточную таблицу значений или вам нужно выполнять дополнительные проверки в процессе обхода строк, можно использовать объект СписокЗначений и метод НайтиЗначение. Разберем, как это работает: мы обходим строки документа, берем значение склада и проверяем, есть ли оно уже в нашем итоговом списке. Если значения нет — добавляем его.
Посмотрим на пример кода:
СписокСкладов = СоздатьОбъект("СписокЗначений");
// Начинаем цикл по строкам табличной части
ВыбратьСтроки();
Пока ПолучитьСтроку() = 1 Цикл
// Предположим, НомерСклада — это реквизит номенклатуры в строке
ТекСклад = СокрЛП(Товар.НомерСклада);
// Проверяем, не добавляли ли мы этот склад ранее
Если СписокСкладов.НайтиЗначение(ТекСклад) = 0 Тогда
СписокСкладов.ДобавитьЗначение(ТекСклад);
КонецЕсли;
КонецЦикла;
// Выводим результат в виде строки
Сообщить("Список складов: " + СписокСкладов.ВСтрокуСРазделителями());
Обратите внимание: При использовании строковых реквизитов крайне важно применять функцию СокрЛП. В системе 1С 7.7 строки "1" и "1 " (с пробелом в конце) считаются разными значениями, что может привести к ложным дублям в вашем списке.
Иногда ситуация осложняется тем, что в одном реквизите "Номер склада" может быть записано сразу несколько значений через запятую (например, "1, 2, 5"). В этом случае простая выгрузка или поиск не помогут — нам нужно сначала "разбить" исходную строку на составляющие, а уже потом проверять их на уникальность. Рассмотрим алгоритм решения такой задачи:
РезСЗ = СоздатьОбъект("СписокЗначений");
ВыбратьСтроки();
Пока ПолучитьСтроку() = 1 Цикл
// Убираем лишние пробелы и заменяем запятые на символы переноса строк
// Это позволит нам работать со строкой как с многострочным текстом
ТекТекст = СтрЗаменить(СокрЛП(Товар.НомерСклада), " ", "");
ТекТекст = СтрЗаменить(ТекТекст, ",", РазделительСтрок);
// Перебираем "подстроки" (каждый номер склада в отдельности)
Для Сч = 1 ПО СтрКолСтр(ТекТекст) Цикл
ТекЗначение = СтрПолучитьСтроку(ТекТекст, Сч);
// Проверяем на пустоту и уникальность
Если (ПустаяСтрока(ТекЗначение) = 0) И (РезСЗ.НайтиЗначение(ТекЗначение) = 0) Тогда
РезСЗ.ДобавитьЗначение(ТекЗначение);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Сообщить("Итоговый уникальный список: " + РезСЗ.ВСтрокуСРазделителями());
Этот подход позволяет нормализовать "грязные" данные, которые часто вводятся пользователями вручную в строковые поля.
Проанализируем ситуацию, когда данных очень много (десятки тысяч строк). В таких случаях стандартные методы 1С могут работать медленно. Разберем несколько советов по оптимизации:
1CPP или FormEx, можно применить объект ИндексированнаяТаблица. Поиск по индексу происходит практически мгновенно по сравнению с обычным НайтиЗначение в СпискеЗначений.Scripting.Dictionary. Этот объект работает на основе хеш-таблиц и обеспечивает очень высокую скорость поиска уникальных ключей.SELECT DISTINCT.Выясним причину выбора конкретного метода. Если ваша задача — разовая обработка документа в несколько сотен строк, используйте Метод 1 (ТЗ.Свернуть) как самый надежный и простой в поддержке. Если же вы работаете со сложными строковыми данными, где значения разделены спецсимволами, ваш выбор — Метод 3 с предварительной нормализацией строки через СтрЗаменить.
В завершение отметим, что для корректной сортировки полученных номеров складов (чтобы склад "10" не шел раньше склада "2") список значений перед выводом можно дополнительно отсортировать методом Сортировать(). Это сделает результат более читаемым для пользователя.