Разберем частую проблему, с которой сталкиваются разработчики в 1С при работе с коллекциями. Когда выполняется последовательный вызов метода Удалить() для таблицы значений по индексам, результат может оказаться неожиданным. Проанализируем причину такого поведения и рассмотрим верные способы удаления строк как на уровне кода, так и на этапе выполнения запроса.
Выясним причину, по которой код вида Результат.Удалить(1); Результат.Удалить(2); работает не так, как ожидает программист.
В платформе 1С нумерация индексов элементов таблицы значений начинается с 0. Первая строка имеет индекс 0, вторая — индекс 1 и так далее. Когда из таблицы физически удаляется строка, все последующие элементы автоматически смещаются вверх, а их индексы уменьшаются на единицу.
Посмотрим на пример: если мы удаляем строку с индексом 1 (вторая строка), бывшая третья строка с индексом 2 моментально занимает освободившийся индекс 1. Вызывая следом Результат.Удалить(2), мы фактически удаляем элемент, который изначально был четвертым по счету (с индексом 3). В итоге часть нужных строк остается нетронутой, а лишние элементы удаляются со смещением.
Чтобы избежать сдвига индексов для еще не обработанных строк, разберем удаление элементов в обратном порядке — от последнего индекса к нулевому. При удалении строки с конца таблицы индексы всех предшествующих строк остаются неизменными.
Рассмотрим пример кода с использованием цикла Пока:
НомерСтрокиТЗ = Результат.Количество() - 1;
Пока НомерСтрокиТЗ >= 0 Цикл
СтрокаТЗ = Результат[НомерСтрокиТЗ];
// Проверяем условие для удаления
Если НеобходимостьУдаления(СтрокаТЗ) Тогда
Результат.Удалить(СтрокаТЗ);
КонецЕсли;
НомерСтрокиТЗ = НомерСтрокиТЗ - 1;
КонецЦикла;
Обратим внимание: передача самого объекта строки СтрокаТЗ в метод Удалить() является наиболее надежным решением.
Разберем еще один подход: сперва найти все строки, подлежащие удалению, и поместить ссылки на них в отдельный массив. После завершения поиска выполним обход полученного массива и удалим элементы из таблицы значений.
Посмотрим, как реализовать этот алгоритм:
МассивУдаляемые = Новый Массив;
// Заполняем массив ссылками на удаляемые строки
Для Каждого СтрокаТЗ Из Результат Цикл
Если УсловиеУдаления(СтрокаТЗ) Тогда
МассивУдаляемые.Добавить(СтрокаТЗ);
КонецЕсли;
КонецЦикла;
// Удаляем собранные строки из таблицы значений
Для Каждого СтрокаДляУдаления Из МассивУдаляемые Цикл
Результат.Удалить(СтрокаДляУдаления);
КонецЦикла;
Важно помнить: напрямую удалять элементы из коллекции внутри цикла Для Каждого Строка Из ТЗ Цикл нельзя, так как это нарушает работу итератора платформы и приводит к пропуску строк.
Если лишние строки легко идентифицировать по конкретным значениям колонок, проанализируем использование встроенных функций таблицы значений.
Рассмотрим отбор через метод НайтиСтроки():
Отбор = Новый Структура("Статус", "Удалить");
МассивСтрок = Результат.НайтиСтроки(Отбор);
Для Каждого СтрокаИзМассива Из МассивСтрок Цикл
Результат.Удалить(СтрокаИзМассива);
КонецЦикла;
Если же требуется сохранить лишь небольшую часть данных, эффективнее не удалять лишние элементы по одному, а скопировать нужные строки в новую таблицу с помощью метода Скопировать(). Метод Удалить() при каждом вызове физически сдвигает массив строк в памяти, поэтому пересборка таблицы через копирование работает значительно быстрее на больших объемах данных.
Часто наиболее правильным решением является изменение логики самого запроса. Если лишние записи попадают в результат на этапе отбора из базы данных, целесообразно исключить их до выгрузки в таблицу значений. Это избавит от необходимости писать дополнительный код обработки в 1С и снизит нагрузку на сервер СУБД.
Рассмотрим основные приемы фильтрации в языке запросов 1С:
Номенклатура.Ссылка В ИЕРАРХИИ (&Группа) или проверку родителя Номенклатура.Родитель = &Группа.ГДЕ НЕ Номенклатура.Наименование ПОДОБНО &ИсключаемыйШаблон.% или _, укажем оператор СПЕЦСИМВОЛ, например: ГДЕ Номенклатура.Наименование ПОДОБНО "%10~%%" СПЕЦСИМВОЛ "~".