К статье
Практический материал

Разработка и Конфигурация · 10 мин

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

Как удалить строку в дереве значений 1С: методы Удалить, циклический обход, удаление подчинённых строк и по условию. Примеры кода и типичные ошибки.

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

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

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

Каждая строка дерева значений (тип СтрокаДереваЗначений) содержит коллекцию подчинённых строк — свойство Строки. Удаление строки выполняется не методом самой строки, а методом Удалить() той коллекции, которой она принадлежит: либо Дерево.Строки для корневых строк, либо Родитель.Строки для вложенных.

Важное следствие иерархии: удаление родительской строки автоматически удаляет все её подчинённые строки любой глубины вложенности. Отдельно удалять «детей» не требуется, но и восстановить их после удаления родителя нельзя — дерево значений не хранит историю изменений.

Удаление конкретной строки

Если у вас уже есть ссылка на строку (например, получена поиском или из текущих данных формы), удаление выглядит так:

СтрокаДерева = Дерево.Строки.Найти(ИскомоеЗначение, "Номенклатура", Истина);

Если СтрокаДерева <> Неопределено Тогда

Родитель = СтрокаДерева.Родитель;

Если Родитель = Неопределено Тогда

Дерево.Строки.Удалить(СтрокаДерева);

Иначе

Родитель.Строки.Удалить(СтрокаДерева);

КонецЕсли;

КонецЕсли;

Обратите внимание на проверку Родитель = Неопределено: у строк верхнего уровня родителя нет, и обращение к Родитель.Строки вызовет ошибку. Это самая частая причина падения кода при удалении корневых элементов.

Удаление строк по условию без сбоя индексов

Классическая ошибка — удалять строки в прямом цикле Для Каждого или Для Инд = 0 По .... После удаления элемента индексы оставшихся смещаются, и цикл пропускает строки либо обращается к несуществующему индексу. Есть два надёжных приёма.

Обход коллекции с конца

КоллекцияСтрок = Дерево.Строки;

Для Инд = 1 По КоллекцияСтрок.Количество() Цикл

Строка = КоллекцияСтрок[КоллекцияСтрок.Количество() - Инд];

Если Строка.Пометка Тогда

КоллекцияСтрок.Удалить(Строка);

КонецЕсли;

КонецЦикла;

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

Сбор строк в массив перед удалением

СтрокиКУдалению = Новый Массив;

Для Каждого Строка Из Дерево.Строки Цикл

Если Строка.Количество = 0 Тогда

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

КонецЕсли;

КонецЦикла;

Для Каждого Строка Из СтрокиКУдалению Цикл

Дерево.Строки.Удалить(Строка);

КонецЦикла;

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

Типичные ошибки
01
Удаление строк в цикле Для Каждого — индексы смещаются, часть строк пропускается или возникает ошибка
02
Обращение к Родитель.Строки у строки верхнего уровня — Родитель равен Неопределено, код падает
03
Удаление родителя без проверки — вместе со строкой исчезают все подчинённые ветви
04
Поиск строки после изменения дерева — ранее полученная ссылка на строку может указывать уже не на тот элемент

Очистка ветвей и всего дерева

Чтобы удалить все подчинённые строки, но оставить саму строку, используйте метод Очистить() её коллекции:

СтрокаДерева.Строки.Очистить(); // удалены все вложенные строки, сама строка осталась

Полная очистка дерева выполняется аналогично на верхнем уровне: Дерево.Строки.Очистить(). Это быстрее, чем удалять строки по одной, и не требует обхода. Если дерево связано с реквизитом формы, после очистки интерфейс обновится автоматически.

Способы удаления

Удалить(Строка)

Удаление одной известной строки. Требует знать коллекцию-владельца (Родитель.Строки или Дерево.Строки)

Удалить(Индекс)

Удаление по индексу. Удобно при обходе с конца, но опасно при прямом цикле

Очистить()

Удаление всех строк коллекции сразу. Самый быстрый вариант для полной очистки ветви или дерева

Удаление строки дерева на форме

Когда дерево отображается в элементе формы «Дерево», текущая строка доступна через Элементы.ИмяДерева.ТекущаяСтрока — это идентификатор строки коллекции данных формы, а не сама строка. Чтобы удалить её, получите объект строки и удалите из родителя:

&НаКлиенте

Процедура УдалитьСтрокуДерева(Команда)

Идентификатор = Элементы.ДеревоДанных.ТекущаяСтрока;

Если Идентификатор = Неопределено Тогда

Возврат;

КонецЕсли;

Строка = ДеревоДанных.НайтиПоИдентификатору(Идентификатор);

Родитель = Строка.ПолучитьРодителя();

Если Родитель = Неопределено Тогда

ДеревоДанных.ПолучитьЭлементы().Удалить(Строка);

Иначе

Родитель.ПолучитьЭлементы().Удалить(Строка);

КонецЕсли;

КонецПроцедуры

Для реквизита формы типа «ДеревоЗначений» в управляемой форме используются методы ПолучитьЭлементы() и ПолучитьРодителя() вместо Строки и Родитель — это данные формы, а не чистый объект дерева. Путать эти два API нельзя: методы не взаимозаменяемы.

Порядок безопасного удаления строки
  1. 1
    Получите ссылку на строку: поиском, через текущую строку формы или обходом
  2. 2
    Определите коллекцию-владельца: Родитель.Строки для вложенных, Дерево.Строки для корневых
  3. 3
    Проверьте, есть ли у строки подчинённые строки, которые не должны быть потеряны
  4. 4
    Вызовите Удалить() у коллекции-владельца
  5. 5
    Проверьте результат: количество строк уменьшилось, структура остальных ветвей не изменилась

Проверка результата и защита от потери данных

После удаления убедитесь, что дерево осталось в ожидаемом состоянии: сравните количество строк до и после, проверьте, что соседние ветви на месте. Если удаление — часть обработки больших данных, имеет смысл сначала скопировать дерево (НовоеДерево = Дерево.Скопировать()), чтобы при ошибке в логике можно было начать заново без повторной выгрузки данных.

Помните, что дерево значений существует только в оперативной памяти сеанса. Ошибочное удаление не затрагивает базу данных напрямую, но если дерево было заполнено трудоёмким запросом и затем использовано для записи данных, восстановить промежуточное состояние будет невозможно — только перезапуском алгоритма.

Контрольные точки

Как проверить результат

Количество строк
Норма

Уменьшилось ровно на число удалённых строк с учётом их подчинённых

Тревожный признак

Изменилось больше или меньше ожидаемого — удалена не та строка или лишние ветви

Соседние строки
Норма

Порядок и содержимое остальных строк не изменились

Тревожный признак

Строки сместились или потеряны — удаление выполнялось в прямом цикле

Отображение на форме
Норма

Строка исчезла из дерева, развёрнутость ветвей сохранена

Тревожный признак

Дерево свернулось или отображает устаревшие данные — нужно обновить элемент формы

Часто задаваемые вопросы

Можно ли удалить строку дерева, не зная её родителя?

Да, но родителя всё равно придётся определить программно через свойство Родитель (или ПолучитьРодителя() для данных формы). Метода самоудаления у строки дерева нет — удаление всегда выполняет коллекция-владелец. Для строк верхнего уровня владельцем является Дерево.Строки.

Почему при удалении в цикле пропускаются строки?

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

Как удалить все строки, удовлетворяющие условию, на всех уровнях дерева?

Напишите рекурсивную процедуру: она обходит коллекцию строк с конца, проверяет условие, удаляет подходящие строки и вызывает сама себя для подчинённых строк каждого оставшегося элемента. Либо используйте НайтиСтроки() с параметром Истина для поиска по всем уровням и удалите найденные строки по одной.

Чем отличается Удалить() от Очистить()?

Удалить() убирает одну конкретную строку (по ссылке или индексу) вместе с её подчинёнными ветвями. Очистить() удаляет все строки коллекции целиком — это быстрее и проще, когда нужно освободить всю ветвь или всё дерево.

Удалится ли строка из базы данных при удалении из дерева значений?

Нет. Дерево значений — объект в оперативной памяти, никак не связанный с базой данных напрямую. Удаление строки влияет только на текущий сеанс. Чтобы изменения попали в базу, нужен отдельный код записи (например, запись элемента справочника или набора регистра).

Как восстановить случайно удалённую строку дерева?

Штатной отмены удаления у дерева значений нет. Варианты: заранее сохранить копию дерева методом Скопировать(), перезаполнить дерево из источника данных или хранить удалённые строки во временной таблице до подтверждения операции. В отладчике можно прервать выполнение до фиксации изменений, но после завершения кода восстановление невозможно.

Итог

Что важно запомнить

Строку дерева значений удаляет коллекция-владелец: Родитель.Строки.Удалить() для вложенных строк и Дерево.Строки.Удалить() для корневых. Удаление родителя уничтожает все подчинённые ветви. В цикле обходите коллекцию с конца или собирайте строки в массив, чтобы не сбить индексы. Для полной очистки ветви используйте Очистить(), а перед массовым удалением делайте копию дерева.