При работе с табличными частями в 1С часто возникает задача удалить строки, которые выделил пользователь. На первый взгляд, задача простая: получаем выделенные строки и удаляем их в цикле. Однако на практике этот подход может привести к ошибкам или неверному поведению, особенно если не до конца понимать разницу между индексом строки и ее идентификатором. Рассмотрим, как правильно и надежно реализовать эту операцию.
Проблема, с которой столкнулся автор, заключалась в непонимании механизма назначения идентификаторов. После удаления нескольких строк и добавления новой, у нее оказался "старый" идентификатор, что вызвало путаницу. Выясним, почему так происходит и как этого избежать.
Прежде чем переходить к коду, важно понять два ключевых термина:
Элементы.ИмяТаблицы.ВыделенныеСтроки возвращает массив именно этих уникальных идентификаторов.Основное заблуждение — считать идентификатор порядковым номером. Платформа управляет идентификаторами самостоятельно, может переиспользовать освободившиеся ID для новых строк, но всегда гарантирует их уникальность в пределах текущей таблицы. На логику назначения идентификаторов закладываться нельзя. Нужно просто использовать их как уникальный ключ для поиска строки.
Это самый надежный, читаемый и безопасный метод. Он исключает любые проблемы, связанные с изменением коллекции, по которой вы, возможно, проходите в цикле. Идея состоит из двух простых шагов.
Шаг 1. Собираем строки, подлежащие удалению.
Сначала мы создаем пустой массив. Затем в цикле обходим массив идентификаторов выделенных строк, который мы получили от элемента формы. Для каждого идентификатора мы находим соответствующую строку данных объекта с помощью метода НайтиПоИдентификатору() и добавляем саму найденную строку (а не ее идентификатор) в наш временный массив.
Шаг 2. Удаляем собранные строки.
После того как первый цикл завершился и все строки для удаления собраны, мы запускаем второй цикл. В нем мы проходим по нашему временному массиву и последовательно удаляем каждую строку из табличной части объекта.
Посмотрим на пример кода. Предположим, у нас есть форма с табличной частью Товары, которая отображает данные табличной части объекта ДокументОбъект.Товары.
// Этот код должен выполняться на сервере
&НаСервере
Процедура УдалитьВыделенныеСтрокиНаСервере()
// Получаем массив идентификаторов выделенных строк из элемента формы
МассивИдентификаторов = Элементы.Товары.ВыделенныеСтроки;
// Шаг 1: Собираем строки для удаления
МассивСтрокКУдалению = Новый Массив;
Для Каждого ИдентификаторСтроки Из МассивИдентификаторов Цикл
// Находим строку в табличной части ОБЪЕКТА по ее идентификатору
НайденнаяСтрока = Объект.Товары.НайтиПоИдентификатору(ИдентификаторСтроки);
Если НайденнаяСтрока <> Неопределено Тогда
// Добавляем в массив не идентификатор, а саму ссылку на строку
МассивСтрокКУдалению.Добавить(НайденнаяСтрока);
КонецЕсли;
КонецЦикла;
// Шаг 2: Удаляем собранные строки
Для Каждого СтрокаКУдалению Из МассивСтрокКУдалению Цикл
Объект.Товары.Удалить(СтрокаКУдалению);
КонецЦикла;
КонецПроцедуры
Почему это работает идеально? Первый цикл, в котором мы ищем строки, только читает данные из основной коллекции Объект.Товары, но не изменяет ее. Все операции удаления происходят во втором цикле, который работает с временным массивом и не зависит от состояния основной коллекции. Это предотвращает пропуск строк и другие непредвиденные ошибки.
Это классический способ избежать проблемы смещения индексов при удалении элементов из коллекции. Суть в том, чтобы перебирать коллекцию не с начала (индекс 0, 1, 2...), а с конца (индекс N-1, N-2, ... 0).
Когда вы удаляете элемент, например, с индексом 5, сдвигаются только элементы с индексами 6, 7 и так далее. А поскольку на следующей итерации цикла вы перейдете к элементу с индексом 4, этот сдвиг никак не повлияет на дальнейшую работу.
Этот метод больше подходит, когда вы удаляете строки по какому-то условию, перебирая всю таблицу, а не по заранее известному набору идентификаторов. Но для общего понимания приведем пример.
// Пример удаления строк по условию с использованием обратного цикла
// Этот код должен выполняться на сервере
Индекс = Объект.Товары.Количество() - 1;
Пока Индекс >= 0 Цикл
ТекущаяСтрока = Объект.Товары[Индекс];
// Пример условия: удаляем все строки с нулевым количеством
Если ТекущаяСтрока.Количество = 0 Тогда
Объект.Товары.Удалить(ТекущаяСтрока);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Применять этот способ для удаления по массиву идентификаторов менее удобно, чем первый, так как вам все равно придется для каждого идентификатора искать его текущий индекс, что неэффективно. Поэтому для задачи удаления выделенных строк первый метод (сбор в массив) является предпочтительным.
ВыделенныеСтроки дает вам именно идентификаторы, которые нужно использовать с методом НайтиПоИдентификатору().Объект.Товары) и операции над ними (Удалить, НайтиПоИдентификатору) должны выполняться в серверном контексте.Следуя этим простым правилам, вы сможете писать надежный и предсказуемый код для работы с табличными частями в 1С.