Как удалить строки из табличной части, где флажок имеет значение Ложь?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Когда мы работаем с данными в 1С, часто возникает необходимость манипулировать строками табличных частей. Одна из распространенных задач — удалить строки, которые не соответствуют определенному условию, например, если булевый реквизит имеет значение Ложь. Мы рассмотрим различные подходы к решению этой задачи, обсудим их преимущества и недостатки, а также проанализируем, какой метод будет наиболее эффективным в зависимости от объема данных и сложности условий.

Метод 1: Удаление строк с конца табличной части в одном цикле

Суть метода: Этот подход предполагает обход строк табличной части, начиная с последней и двигаясь к первой. Если строка соответствует условию для удаления, мы удаляем ее. Удаление с конца предотвращает проблемы со смещением индексов строк, которые могли бы возникнуть при удалении с начала или середины таблицы, и позволяет нам корректно перебирать все элементы.

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

Преимущества:

  1. Простота реализации: Не требует создания дополнительных коллекций для хранения ссылок на строки для удаления.
  2. Надежность: Предотвращает ошибки, связанные со смещением индексов строк при удалении, так как удаляемые элементы не влияют на индексы еще не обработанных элементов.
  3. Универсальность: Позволяет обрабатывать сложные условия удаления, не ограничиваясь простым равенством.

Недостатки:

  1. Производительность: На очень больших объемах данных (сотни тысяч и миллионы строк) удаление каждой строки по отдельности может быть медленным, так как каждая операция удаления может вызывать перестроение внутренних структур данных и индексов табличной части. Это может стать критическим узким местом при пакетной обработке.

Пример кода:


// Предположим, у нас есть табличная часть "МояТЧ"
// и в ней реквизит "Выбор" типа Булево

Для ИндексСтроки = МояТЧ.Количество() - 1 По 0 Цикл
    СтрокаТЧ = МояТЧ[ИндексСтроки];
    Если СтрокаТЧ.Выбор = Ложь Тогда
        МояТЧ.Удалить(СтрокаТЧ);
    КонецЕсли;
КонецЦикла;

Разберем этот код подробнее. Мы используем цикл Для ... По ... Цикл, который итерирует от предпоследнего индекса (МояТЧ.Количество() - 1) до первого (0). Внутри цикла мы получаем текущую строку табличной части по индексу МояТЧ[ИндексСтроки]. Затем проверяем условие: если значение реквизита Выбор равно Ложь, то мы вызываем метод МояТЧ.Удалить(СтрокаТЧ), который удаляет данную строку из табличной части.

Подробнее остановимся на этом способе. Использование цикла Для ИндексСтроки = МояТЧ.Количество() - 1 По 0 Цикл является ключевым. Если бы мы перебирали строки от начала к концу (от 0 до МояТЧ.Количество() - 1) и удаляли элементы, то при каждом удалении строки все последующие строки сдвигались бы, и их индексы менялись. Это привело бы к тому, что либо мы бы пропускали строки, либо, наоборот, обрабатывали бы одни и те же строки дважды, либо получали бы ошибку "Индекс за пределами диапазона". Удаление с конца полностью исключает эту проблему, так как удаляемая строка не влияет на индексы предыдущих (еще не обработанных) строк. Мы используем метод МояТЧ.Удалить(СтрокаТЧ), который является стандартным для коллекций строк табличной части и таблиц значений. Этот метод удаляет указанную строку из коллекции. Этот подход гарантирует, что при удалении строки индексы последующих (уже обработанных) строк не изменятся, что исключает ошибки доступа к данным.

Метод 2: Сбор строк для удаления в массив и последующее удаление

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

Когда применять: Этот метод также подходит для небольших и средних табличных частей. Его можно рассмотреть, когда необходимо сначала полностью проанализировать табличную часть и только потом приступить к модификации. С точки зрения производительности он схож с предыдущим методом при большом количестве удалений, так как все равно предполагает множественные операции удаления. Иногда его выбирают из соображений читаемости кода или когда нужно собрать информацию о нескольких группах строк перед выполнением различных действий.

Преимущества:

  1. Гибкость: Позволяет сначала полностью проанализировать табличную часть, собрать все "кандидаты" на удаление, а затем уже выполнять операции модификации. Это может быть полезно в более сложных сценариях, когда на основе собранных данных нужно принять дополнительные решения перед удалением.
  2. Потенциальная читаемость: Некоторые разработчики считают этот подход более читаемым, так как он разделяет логику поиска и логику удаления.

Недостатки:

  1. Производительность: Как и предыдущий метод, страдает от множественных операций удаления на больших объемах данных.
  2. Дополнительные затраты памяти: Требует создания и заполнения дополнительного массива, что может незначительно увеличить потребление памяти для очень больших табличных частей.

Пример кода:


// Предположим, у нас есть табличная часть "МояТЧ"
// и в ней реквизит "Выбор" типа Булево

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

// Шаг 1: Сбор строк для удаления
Для Каждого СтрокаТЧ Из МояТЧ Цикл
    Если СтрокаТЧ.Выбор = Ложь Тогда
        МассивСтрокДляУдаления.Добавить(СтрокаТЧ);
    КонецЕсли;
КонецЦикла;

// Шаг 2: Удаление строк из массива
Для Каждого СтрокаТЧ Из МассивСтрокДляУдаления Цикл
    МояТЧ.Удалить(СтрокаТЧ);
КонецЦикла;

Разберем логику этого подхода. Первым делом мы создаем временный Массив. В 1С Массив — это динамический список значений, который идеально подходит для временного хранения ссылок на объекты. Мы используем цикл Для Каждого СтрокаТЧ Из МояТЧ Цикл, который удобно перебирает все элементы коллекции, не требуя работы с индексами. В этом цикле мы не модифицируем саму табличную часть, а только собираем ссылки на те объекты СтрокаТЧ, которые нам нужно удалить, добавляя их в МассивСтрокДляУдаления с помощью метода Добавить().

После того как первый цикл завершен, мы имеем полный список строк, подлежащих удалению. Затем мы запускаем второй цикл Для Каждого СтрокаТЧ Из МассивСтрокДляУдаления Цикл. В этом цикле мы уже обращаемся к оригинальной табличной части МояТЧ и для каждой строки из нашего временного массива вызываем метод МояТЧ.Удалить(СтрокаТЧ). Важно понимать, что в массиве хранятся ссылки на оригинальные строки, поэтому метод Удалить() будет работать корректно, удаляя нужные объекты из МояТЧ. Хотя этот метод требует двух проходов по данным и временного расхода памяти на массив, для небольших таблиц он вполне адекватен и может быть предпочтительным в случаях, когда нужна дополнительная логика между поиском и удалением.

Метод 3: Формирование новой табличной части с нужными строками (самый производительный способ)

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

Когда применять: Этот подход особенно эффективен, когда табличная часть содержит большое количество строк (тысячи, десятки тысяч и более), и необходимо удалить значительную часть этих строк. Он минимизирует операции удаления отдельных элементов, которые могут быть очень ресурсоемкими из-за необходимости перестроения индексов и внутренних структур данных.

Преимущества:

  1. Высокая производительность: Значительно быстрее при работе с большими табличными частями, так как операции "удалить все" (Очистить()) и "добавить много строк" (копирование) обычно оптимизированы и выполняются гораздо эффективнее, чем множество одиночных удалений.
  2. Избегает проблем с индексами: Мы фактически не удаляем строки по одной, а заменяем содержимое табличной части, что исключает накладные расходы на постоянное обновление индексов.
  3. Надежность: Менее подвержен ошибкам, связанным с изменением структуры коллекции во время обхода.

Недостатки:

  1. Дополнительные затраты памяти: В течение короткого времени в памяти будут существовать две табличные части (исходная и временная), что может потребовать больше оперативной памяти. Однако это обычно не является критической проблемой для большинства сценариев.
  2. Немного сложнее код: Требует создания дополнительной временной коллекции и работы с ней.

Пример кода:


// Предположим, у нас есть табличная часть "МояТЧ"
// и в ней реквизит "Выбор" типа Булево

// Создаем временную таблицу значений
ВременнаяТЗ = Новый ТаблицаЗначений;

// Определяем колонки в новой ТЗ, совпадающие с реквизитами ТЧ
Для Каждого КолонкаТЧ Из МояТЧ.Колонки Цикл
    ВременнаяТЗ.Колонки.Добавить(КолонкаТЧ.Имя, КолонкаТЧ.ТипЗначения);
КонецЦикла;

// Копируем только нужные строки в новую ТЗ
Для Каждого СтрокаТЧ Из МояТЧ Цикл
    Если СтрокаТЧ.Выбор = Истина Тогда // Копируем выбранные строки
        НоваяСтрокаТЗ = ВременнаяТЗ.Добавить();
        // Заполняем реквизиты новой строки из исходной
        ЗаполнитьЗначенияСвойств(НоваяСтрокаТЗ, СтрокаТЧ);
    КонецЕсли;
КонецЦикла;

// Очищаем исходную табличную часть
МояТЧ.Очистить();

// Заполняем исходную табличную часть данными из временной ТЗ
Для Каждого СтрокаТЗ Из ВременнаяТЗ Цикл
    НоваяСтрокаТЧ = МояТЧ.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрокаТЧ, СтрокаТЗ);
КонецЦикла;

Остановимся на деталях этого высокопроизводительного метода. Сначала мы объявляем новый объект ТаблицаЗначений, который будет служить нашей временной коллекцией. Критически важный шаг — это определение колонок для ВременнаяТЗ. Если мы не добавим колонки, то при попытке скопировать данные методом ЗаполнитьЗначенияСвойств() или вручную, мы получим ошибку, так как в таблице значений не будет соответствующих полей. Мы используем цикл Для Каждого КолонкаТЧ Из МояТЧ.Колонки Цикл для автоматического переноса всех колонок (их имен и типов) из исходной табличной части в нашу временную ТаблицаЗначений. Это гарантирует полную совместимость структуры.

Далее, мы обходим исходную табличную часть МояТЧ. Для каждой строки мы проверяем условие сохранения: если СтрокаТЧ.Выбор = Истина (то есть, это строка, которую мы хотим оставить), мы выполняем следующие действия:

  1. Создаем новую строку в ВременнаяТЗ с помощью метода ВременнаяТЗ.Добавить().
  2. Используем универсальный метод ЗаполнитьЗначенияСвойств(Приемник, Источник). Этот метод позволяет эффективно скопировать значения всех одноименных свойств из объекта-источника (СтрокаТЧ) в объект-приемник (НоваяСтрокаТЗ). Это существенно упрощает код и делает его более устойчивым к изменениям в структуре табличной части.

После того как все нужные строки скопированы во ВременнаяТЗ, мы приступаем к очистке. Вызов МояТЧ.Очистить() — это очень быстрая операция, которая удаляет все элементы из табличной части без многократных перестроений индексов. Фактически, она просто обнуляет внутренний счетчик элементов и освобождает память, связанную с ними.

На последнем этапе мы снова используем цикл Для Каждого СтрокаТЗ Из ВременнаяТЗ Цикл, чтобы перенести сохраненные строки обратно в МояТЧ. Для каждой строки из ВременнаяТЗ мы добавляем новую строку в МояТЧ и снова используем ЗаполнитьЗначенияСвойств() для быстрого копирования данных. Этот метод является эталоном производительности, особенно когда нам нужно удалить большую часть данных, и он позволяет избежать проблем с "тяжеловесными" операциями удаления по одной.

Метод 4: Использование метода НайтиСтроки()

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

Когда применять: Хорошо подходит для небольших и средних табличных частей, когда требуется удалить небольшое количество строк по простому условию. Если найденных строк много, производительность может быть невысокой из-за множественных операций удаления, как и в первых двух методах.

Преимущества:

  1. Краткость кода: Позволяет быстро получить список строк, соответствующих условию.
  2. Читаемость: Код с использованием НайтиСтроки() часто более лаконичен и понятен для простых условий.

Недостатки:

  1. Ограниченность условий: Метод НайтиСтроки() работает только с отбором по структуре, то есть с простыми условиями равенства. Для сложных условий (например, "ИЛИ", "НЕ", "больше", "меньше", сравнение с другими полями) он не подходит.
  2. Производительность: Если найденных строк много, и все они удаляются по отдельности, это может быть неэффективно.
  3. Проблемы с индексами: Как и в случае с циклом, удаление строк "с начала" или "из середины" коллекции, полученной через НайтиСтроки(), при последующем обходе основной табличной части или если мы хотим удалить элементы из найденной коллекции, может привести к смещению индексов. Поэтому, если используем этот метод для удаления, лучше всего сочетать его с обходом найденных строк в обратном порядке (как в Методе 1) или сначала собрать их в массив, а потом удалить.

Пример кода:


// Предположим, у нас есть табличная часть "МояТЧ"
// и в ней реквизит "Выбор" типа Булево

Отбор = Новый Структура("Выбор", Ложь);
НайденныеСтроки = МояТЧ.НайтиСтроки(Отбор);

// Удаляем найденные строки, обходя их с конца
Для Индекс = НайденныеСтроки.Количество() - 1 По 0 Цикл
    МояТЧ.Удалить(НайденныеСтроки[Индекс]);
КонецЦикла;

Рассмотрим метод НайтиСтроки() подробнее. Он представляет собой удобный инструмент для фильтрации строк в коллекциях 1С. Для его использования мы создаем объект Структура, где ключами являются имена реквизитов табличной части, по которым мы хотим осуществлять отбор, а значениями — искомые значения этих реквизитов. В нашем случае это Новый Структура("Выбор", Ложь), что означает "найти все строки, где реквизит 'Выбор' равен Ложь".

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

Поскольку мы хотим удалить эти строки, мы сталкиваемся с той же проблемой смещения индексов, что и в Методе 1. Поэтому, чтобы гарантировать корректное удаление без ошибок, мы обходим коллекцию НайденныеСтроки с конца, используя цикл Для Индекс = НайденныеСтроки.Количество() - 1 По 0 Цикл. Внутри цикла мы обращаемся к элементу коллекции НайденныеСтроки[Индекс], который является ссылкой на строку в МояТЧ, и передаем ее в метод МояТЧ.Удалить(). Этот метод позволяет нам быстро найти нужные строки по простому условию, а затем эффективно удалить их. Однако, если условия поиска становятся сложнее (например, требуется фильтрация по нескольким полям с операторами "ИЛИ", или использование функций), метод НайтиСтроки() уже не подойдет, и придется возвращаться к ручному обходу.

Сравнение методов и общие рекомендации

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

  1. Объем данных: Для небольших и средних табличных частей (до нескольких тысяч строк) любой из первых двух методов (обход с конца или сбор в массив) будет приемлемым. Метод НайтиСтроки() также подойдет, если условия простые и количество удаляемых строк невелико.
  2. Производительность: Если мы работаем с большими объемами данных (десятки тысяч, сотни тысяч или миллионы строк) или если требуется удалить большую часть строк из табличной части, то метод формирования новой табличной части (Метод 3) является наиболее предпочтительным и производительным. Операции очистки и последующего заполнения/копирования значительно быстрее, чем множество индивидуальных удалений.
  3. Сложность условий: Если условия удаления очень сложные, содержат логические "ИЛИ", сравнения между полями или требуют дополнительной логики, то прямой обход в цикле (Метод 1 или 2) будет более гибким, чем использование НайтиСтроки().
  4. Читаемость и поддерживаемость кода: Для простых задач методы с прямым циклом или НайтиСтроки() могут быть более лаконичными и понятными. Однако, при росте сложности, универсальность Метода 3 порой оправдывает его чуть большую избыточность.

Важное замечание: Если нам нужно удалить все строки из табличной части, то самым быстрым и правильным решением будет просто вызвать метод МояТЧ.Очистить().

МояТЧ.Очистить();

В итоге, универсального "серебряной пули" не существует. Мы должны анализировать конкретную ситуацию, объемы данных и требования к производительности, чтобы выбрать наиболее оптимальный способ. Однако, для большинства случаев, где производительность критична, копирование нужных строк в новую табличную часть и замена старой является золотым стандартом.

← На главную