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

Программист 1С v8.3 (Обычные формы) Промышленность, строительство и АПК
← На главную

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

Анализ проблемы: почему возникает ошибка при удалении

Рассмотрим ситуацию, описанную в исходном вопросе. Программист пытается удалить строку напрямую из корневой коллекции строк дерева:

ДеревоОборудования.Строки.Удалить(СтрокаДляУдаления);

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

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

Решение проблемы: удаление через родительский узел

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


// Сначала найдем нужную строку в дереве
СтрокаДляУдаления = ДеревоОборудования.Строки.Найти(ТК.Оборудование, "Оборудование", Истина);

// Проверим, была ли найдена строка
Если СтрокаДляУдаления <> Неопределено Тогда
    // Обращаемся к родителю найденной строки и удаляем её из его коллекции
    СтрокаДляУдаления.Родитель.Строки.Удалить(СтрокаДляУдаления);
КонецЕсли;

Однако здесь кроется ловушка. Если найденная строка находится на самом верхнем уровне (в корне), то свойство Родитель вернет Неопределено. Попытка вызвать Родитель.Строки в этом случае приведет к аварийному завершению программы.

Создание универсального алгоритма удаления

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

Проанализируем следующий алгоритм:


СтрокаДляУдаления = ДеревоОборудования.Строки.Найти(ЗначениеПоиска, "ИмяКолонки", Истина);

Если СтрокаДляУдаления <> Неопределено Тогда
    РодительУзла = СтрокаДляУдаления.Родитель;
    
    Если РодительУзла = Неопределено Тогда
        // Строка находится на верхнем уровне
        ДеревоОборудования.Строки.Удалить(СтрокаДляУдаления);
    Иначе
        // Строка является вложенной
        РодительУзла.Строки.Удалить(СтрокаДляУдаления);
    КонецЕсли;
КонецЕсли;

Этот подход гарантирует отсутствие ошибок вне зависимости от структуры вашего дерева. Мы сначала выясняем местоположение строки, а затем применяем метод удаления к соответствующему контексту.

Нюансы удаления строк в цикле

Выясним еще одну важную деталь: как удалять сразу несколько строк, соответствующих условию? Часто программисты совершают ошибку, используя цикл Для Каждого. Проблема в том, что при удалении элемента из коллекции индексы всех последующих элементов смещаются на единицу «вверх».

Рассмотрим правильный подход — обход коллекции с конца. Посмотрим на пример кода для очистки всех строк определенного уровня, удовлетворяющих условию:


КоллекцияСтрок = РодительскийУзел.Строки;
Для Сч = КоллекцияСтрок.Количество() - 1 По 0 Цикл
    ТекущаяСтрока = КоллекцияСтрок[Сч];
    Если ТекущаяСтрока.ПометкаУдаления Тогда
        КоллекцияСтрок.Удалить(Сч); // Удаляем по индексу
    КонецЕсли;
КонецЦикла;

При таком способе смещение индексов не влияет на еще не обработанные элементы, так как мы движемся к началу списка. Это критически важно для стабильной работы алгоритмов обработки данных.

Особенности работы в Управляемых формах

В современных конфигурациях на платформе 8.3 (Управляемые формы) мы чаще работаем не напрямую с объектом ДеревоЗначений, а с реквизитом формы типа ДанныеФормыДерево. Здесь есть свои особенности:

  1. Использование идентификаторов: Каждая строка в ДанныеФормыДерево имеет уникальный идентификатор. Иногда удобнее удалять строку по её ID через методы элемента формы.
  2. Обновление интерфейса: Если вы удаляете строку в серверном коде через объектную модель, изменения отобразятся на экране автоматически после завершения вызова сервера.
  3. Метод ТекущиеДанные: Чтобы удалить строку, на которой в данный момент стоит курсор пользователя, используйте конструкцию:
    
    ТекущаяСтрока = Элементы.ТаблицаДерева.ТекущиеДанные;
    Если ТекущаяСтрока <> Неопределено Тогда
        Идентификатор = ТекущаяСтрока.ПолучитьИдентификатор();
        Элементы.ТаблицаДерева.Удалить(Идентификатор);
    КонецЕсли;
    

Рекурсивный поиск и удаление

Метод Найти(), который использовал автор темы, удобен тем, что он выполняет поиск рекурсивно (если третий параметр установлен в Истина). Но помните, что он возвращает только **первое** найденное вхождение. Если в вашем дереве может быть несколько строк с одинаковым значением ключа, и вам нужно удалить их все, метод Найти() придется вызывать в цикле, пока он не вернет Неопределено.

В качестве альтернативы проанализируем использование метода НайтиСтроки(). Этот метод возвращает массив всех строк, соответствующих критериям, но он не умеет искать по всей глубине дерева автоматически. Его удобно использовать, если вам нужно очистить конкретный уровень подчинения.

Заключение

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

← На главную