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