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