Очистка массива во встроенном языке 1С 8.3: полная и выборочная

· 10 мин

Содержание

Очистить массив в 1С целиком можно одной строкой: МойМассив.Очистить();. Метод удаляет все элементы, после него Количество() возвращает 0, а ВГраница() возвращает -1. Сам объект остаётся тем же, поэтому все переменные, которые на него ссылаются, тоже видят пустой массив.

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

Базовые способы полностью очистить массив

Полная очистка массива в 1С делается двумя путями. Они дают одинаково пустой массив, но ведут себя по-разному, если на массив есть другие ссылки.

Способ 1. Метод Очистить()

Товары = Новый Массив;
Товары.Добавить("Молоко");
Товары.Добавить("Хлеб");

Товары.Очистить();
// Товары.Количество() = 0

Метод ничего не возвращает и не принимает параметров. Он меняет тот самый объект, на который указывает переменная.

Способ 2. Присвоить новый массив

Товары = Новый Массив;

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

Чем отличаются два способа

Разница видна, когда массив хранится в двух переменных или передан в процедуру:

Исходный = Новый Массив;
Исходный.Добавить(1);
Исходный.Добавить(2);

Копия = Исходный;       // это не копия, а вторая ссылка на тот же объект

Копия.Очистить();
// Исходный.Количество() = 0 — очистился и он

Исходный.Добавить(5);
Копия = Новый Массив;
// Исходный.Количество() = 1 — его новый массив не задел
СпособЧто происходитКогда выбирать
Очистить()Тот же объект становится пустымМассив передан в процедуру или лежит в структуре, и вызывающий код должен увидеть пустоту
= Новый МассивПеременная получает новый объект, старый не трогаетсяНужно начать заново, не затрагивая тех, кто держит старый массив
= НеопределеноПеременная вообще перестаёт быть массивомОсвободить переменную; дальше Добавить() вызвать уже нельзя

Если массив пришёл параметром и его нужно опустошить для вызывающего кода, пишите только Параметр.Очистить(). Присваивание Параметр = Новый Массив внутри процедуры с Знач вызывающий код не увидит вообще.

Обнулить значения, но сохранить размер

Иногда под «очистить» имеют в виду другое: оставить 10 ячеек, но стереть в них данные. Для этого есть два варианта:

// Вариант А: новый массив на 10 элементов, все равны Неопределено
Ячейки = Новый Массив(10);

// Вариант Б: пройти по существующему и сбросить значения
Для Индекс = 0 По Ячейки.ВГраница() Цикл
	Ячейки[Индекс] = 0;
КонецЦикла;

Количество элементов при этом не меняется, Ячейки.Количество() по-прежнему вернёт 10.

Выборочное удаление элементов массива в цикле

Метод Удалить(Индекс) убирает один элемент по номеру. Индексы начинаются с нуля. После удаления все элементы правее сдвигаются на одну позицию влево, и массив становится короче на единицу.

Почему прямой цикл ломается

Две частые ловушки:

  • 🔁 Цикл Для Индекс = 0 По Массив.ВГраница() вычисляет верхнюю границу один раз, в начале. После первого удаления массив короче, и на последних итерациях код обращается к несуществующему индексу. Итог — ошибка Значение индекса выходит за пределы диапазона.
  • ⏭️ Цикл Для Каждого при удалении текущего элемента перескакивает через соседний: тот сдвинулся на место удалённого, а обход уже ушёл дальше. Ошибки нет, но часть элементов остаётся в массиве.

Правильно: обход с конца

Если идти от последнего индекса к нулевому, удаление не сдвигает те элементы, которые ещё не проверены.

// Удаляем из массива все отрицательные числа
Числа = Новый Массив;
Числа.Добавить(5);
Числа.Добавить(-2);
Числа.Добавить(-7);
Числа.Добавить(3);

ПоследнийИндекс = Числа.ВГраница();   // 3, фиксируем до начала удаления
Для Сч = 0 По ПоследнийИндекс Цикл
	Индекс = ПоследнийИндекс - Сч;     // 3, 2, 1, 0
	Если Числа[Индекс] < 0 Тогда
		Числа.Удалить(Индекс);
	КонецЕсли;
КонецЦикла;
// Числа: 5, 3

Индекс считается от зафиксированной границы, а не от текущего Количество(). Поэтому удаление на шаге 2 не влияет на шаги 3 и 4.

Тот же обход через Пока:

Индекс = Числа.ВГраница();
Пока Индекс >= 0 Цикл
	Если Числа[Индекс] < 0 Тогда
		Числа.Удалить(Индекс);
	КонецЕсли;
	Индекс = Индекс - 1;
КонецЦикла;

Удаление по значению

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

// Убрать все вхождения строки "Брак"
Индекс = Статусы.Найти("Брак");
Пока Индекс <> Неопределено Цикл
	Статусы.Удалить(Индекс);
	Индекс = Статусы.Найти("Брак");
КонецЦикла;

Для ссылок (СправочникСсылка, ДокументСсылка) Найти() тоже работает: одинаковые ссылки равны между собой.

Фильтрация в новый массив

Когда удалить нужно много элементов, проще собрать то, что остаётся:

Оставить = Новый Массив;
Для Каждого Элемент Из Числа Цикл
	Если Элемент >= 0 Тогда
		Оставить.Добавить(Элемент);
	КонецЕсли;
КонецЦикла;
Числа = Оставить;

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

Какой способ очистки быстрее

Массив в 1С хранит элементы подряд. Отсюда простое правило: удаление из конца дёшево, удаление из середины и начала заставляет сдвигать всё, что правее.

МетодСложностьГде оправдан
Очистить()Один вызов, без перебораПолная очистка, всегда первый выбор
= Новый МассивОдин вызовПолная очистка без влияния на другие ссылки
Удалить() в обратном циклеКаждое удаление сдвигает хвостУдаляется немного элементов из большого массива
Найти() + Удалить() в циклеКаждый Найти() заново просматривает массивОдно-два значения в небольшом массиве
Сборка нового массиваОдин проходУдаляется большая доля элементов, массив на десятки тысяч строк
Удаление с начала: Удалить(0) в циклеХудший вариант: сдвиг всего массива на каждом шагеНе использовать для очистки

Самый частый антипаттерн — очистка через Пока Массив.Количество() > 0 Цикл Массив.Удалить(0). Результат тот же, что у Очистить(), но на больших массивах время растёт квадратично.

Если массив на сотни тысяч элементов собирается из запроса, а потом фильтруется в коде, проверьте, нельзя ли отфильтровать в самом запросе условием ГДЕ. Это почти всегда быстрее любой обработки массива.

Готовые функции БСП

В типовых конфигурациях на Библиотеке стандартных подсистем (БП 3.0, ЗУП 3.1, УТ 11 и других) есть общий модуль ОбщегоНазначенияКлиентСервер с готовыми функциями:

  • 🧹 УдалитьВсеВхожденияЗначенияИзМассива(Массив, Значение) — убирает все вхождения значения;
  • 🧩 СвернутьМассив(Массив) — возвращает массив без повторов;
  • ➖ РазностьМассивов(Массив, ВычитаемыйМассив) — возвращает элементы первого массива, которых нет во втором.

Перед использованием откройте модуль в конфигураторе и проверьте сигнатуру в вашем релизе БСП: возвращает ли функция новый массив или меняет переданный. В самописной конфигурации без БСП этих функций нет.

Значения, ссылки и память при очистке массива

Массив хранит не копии объектов, а ссылки на них. Это определяет, что именно «исчезает» при очистке.

Очистка не удаляет данные из базы

Если в массиве лежат СправочникСсылка или ДокументСсылка, Очистить() убирает из памяти только сами ссылки. Элементы справочника и документы в базе остаются на месте. Для удаления объектов из базы нужен отдельный код с ПолучитьОбъект() и записью пометки удаления — массив тут ни при чём.

Объекты в памяти

Если в массиве лежат СправочникОбъект, таблицы значений, структуры или другие массивы, после очистки они освобождаются, когда на них не остаётся других ссылок. Платформа считает ссылки на объекты.

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

Для Каждого Узел Из Узлы Цикл
	Узел.Родитель = Неопределено;   // разрываем обратную ссылку
КонецЦикла;
Узлы.Очистить();

Передача в процедуры

Массив — объект, поэтому в процедуру передаётся ссылка на него, даже с ключевым словом Знач. Разница только в присваивании:

Код внутри процедурыБез ЗначС Знач
Массив.Очистить()Вызывающий код видит пустой массивВызывающий код тоже видит пустой массив
Массив = Новый МассивУ вызывающего кода тоже новый массивУ вызывающего кода старый массив, без изменений

Если процедура должна работать с копией и не трогать оригинал, создайте копию явно: Копия = Новый Массив(Новый ФиксированныйМассив(Оригинал)); или перенесите элементы циклом.

Копия таким способом поверхностная. Элементы-структуры и вложенные массивы останутся общими: изменение структуры в копии изменит её и в оригинале.

Фиксированный массив и многомерный массив

У ФиксированныйМассив нет методов изменения. Вызов Очистить() или Удалить() даст ошибку Метод объекта не обнаружен (Очистить). Такой массив не чистят, а заменяют: делают обычный Новый Массив(ФиксированныйМассив), правят его и при необходимости снова фиксируют.

Многомерный массив Новый Массив(3, 4) — это массив из трёх массивов по четыре элемента. Очистить() у внешнего массива удаляет строки целиком. Чтобы стереть только содержимое, проходите по каждой строке:

Матрица = Новый Массив(3, 4);
Для Каждого Строка Из Матрица Цикл
	Строка.Очистить();     // внешних строк по-прежнему 3, в каждой 0 элементов
КонецЦикла;

Ошибки при работе с пустыми массивами

После очистки массив пуст, и часть привычного кода перестаёт работать.

Диагностика: выберите симптом

Проверка на пустоту делается одним условием: Если Массив.Количество() = 0 Тогда. Сравнение Массив = Новый Массив не работает : это два разных объекта, и результат всегда Ложь.

Практики, которые экономят время на отладке

Несколько привычек, после которых проблемы с очисткой массивов почти не возникают:

  1. Полную очистку делайте только через Очистить(), а не циклом. Это короче и быстрее.
  2. Удаляйте с конца или собирайте новый массив. Прямой цикл с Удалить() — источник большинства ошибок.
  3. Фиксируйте границу до цикла в переменной, а не вызывайте ВГраница() или Количество() для пересчёта индекса внутри.
  4. Решите заранее, нужна ли общая ссылка. Если массив отдан наружу, Очистить(). Если нет, Новый Массив.
  5. Не храните служебные массивы в глобальных переменных модуля без нужды. Они живут весь сеанс и держат в памяти всё, что в них лежит.
  6. Для частого поиска используйте Соответствие. Если массив служит списком «уже обработанных» значений и в нём постоянно ищут через Найти(), соответствие с ключом-значением проверяет наличие быстрее.
  7. Документируйте в комментарии к процедуре, меняет ли она переданный массив. Это снимает половину вопросов при чтении чужого кода.

Если массив передаётся между клиентом и сервером, на другую сторону уходит его копия. Очистка массива на сервере не очистит переменную формы на клиенте, пока значение не вернётся обратно через параметр или результат функции.

Частые вопросы

Чем Очистить() отличается от Новый Массив?
Очистить() опустошает тот же объект, и все ссылки на него видят пустой массив. Новый Массив создаёт другой объект, старый остаётся у тех, кто на него ссылается.
Можно ли удалить элемент массива по значению одной командой?
Встроенного метода нет: Удалить() принимает только индекс. Найдите индекс через Найти() или используйте ОбщегоНазначенияКлиентСервер.УдалитьВсеВхожденияЗначенияИзМассива в конфигурациях на БСП.
Удаляет ли очистка массива ссылок элементы из базы?
Нет. В массиве лежат только ссылки, и очистка убирает их из памяти. Записи справочников и документы в базе не меняются.
Как очистить массив, который лежит в структуре?
Обратитесь к нему через ключ: Параметры.Список.Очистить(). Структура сохранит тот же объект, но уже пустой.
Почему ВГраница() у пустого массива равна -1?
Она возвращает индекс последнего элемента. У пустого массива элементов нет, поэтому значение на единицу меньше нуля, и цикл от 0 до ВГраница() не выполняется.