Замена, вставка и удаление элементов массива во встроенном языке 1С:Предприятия 8.3
Содержание
Чтобы изменить значение в массиве 1С, присвойте новое значение элементу по индексу: Массив[2] = 150;. Тот же результат даёт метод Массив.Установить(2, 150);. Индексы начинаются с нуля, последний индекс возвращает Массив.ВГраница().
Это всё, что нужно для замены одного элемента. Сложности начинаются, когда значение надо поменять внутри цикла, вставить новое в середину, удалить лишнее или добраться до поля структуры, которая лежит в массиве. Ниже по каждому случаю есть рабочий код и разбор подводных камней. Всё относится к платформе 8.3 и одинаково работает в любой конфигурации: массив — объект встроенного языка, а не конфигурации.
Как устроен массив и где его индексы
Массив в 1С — упорядоченная коллекция значений любого типа. В одном массиве могут лежать число, строка, ссылка на документ и структура одновременно. Размер меняется на ходу.
Главное, что нужно держать в голове при изменении элементов:
- 🔢 первый элемент имеет индекс
0, последний —Количество() - 1; - 📏
ВГраница()возвращает последний индекс, для пустого массива —-1; - 🔗 массив — объект ссылочного типа: две переменные могут указывать на один и тот же массив;
- 🔒
ФиксированныйМассивизменить нельзя вообще, у него нет методов записи.
Пример, на котором строятся все дальнейшие разделы:
Цены = Новый Массив;
Цены.Добавить(100); // индекс 0
Цены.Добавить(250); // индекс 1
Цены.Добавить(80); // индекс 2
Сообщить(Цены.Количество()); // 3
Сообщить(Цены.ВГраница()); // 2
Методы, которые меняют содержимое массива:
| Метод | Что делает | Размер массива |
|---|---|---|
Массив[Индекс] = Значение | заменяет элемент | не меняется |
Установить(Индекс, Значение) | заменяет элемент | не меняется |
Добавить(Значение) | дописывает в конец | +1 |
Вставить(Индекс, Значение) | вставляет в позицию, остальные сдвигает | +1 |
Удалить(Индекс) | удаляет элемент, остальные сдвигает | −1 |
Очистить() | удаляет все элементы | 0 |
Для чтения есть Получить(Индекс) и Найти(Значение). Последний возвращает индекс первого совпадения или Неопределено.
Замена элемента по индексу через квадратные скобки
Самый короткий и самый частый способ. Квадратные скобки работают и на чтение, и на запись:
Цены[1] = 300; // было 250, стало 300
Цены[0] = Цены[0] * 1.2; // было 100, стало 120
Цены[Цены.ВГраница()] = 0; // последний элемент обнулили
Тип нового значения может отличаться от старого. Платформа не проверяет, что в массиве «были числа»: Цены[2] = "нет в наличии"; выполнится без ошибки. Ошибка вылезет позже — там, где код попытается умножить эту строку на число.
Индекс за пределами массива вызывает исключение Индекс находится за границами массива. Записать в Цены[3], если в массиве три элемента, нельзя. Новый элемент добавляют через Добавить или Вставить, а не присваиванием.
Если индекс приходит извне (из параметра, из результата Найти), проверьте его до записи:
Индекс = Цены.Найти(80);
Если Индекс <> Неопределено Тогда
Цены[Индекс] = 95;
КонецЕсли;
Без проверки при отсутствии значения Найти вернёт Неопределено, и обращение Цены[Неопределено] упадёт с ошибкой.
Метод Установить вместо «Записать»
Метода Записать у массива нет. Его ищут по аналогии с объектами базы данных, где Записать() сохраняет документ или элемент справочника. Массив живёт только в памяти, сохранять его некуда, поэтому для замены элемента служит Установить:
Цены.Установить(1, 300);
По результату это то же самое, что Цены[1] = 300. Различий в поведении нет: тот же контроль границ, та же ошибка при выходе за пределы.
Когда Установить удобнее скобок:
- 🧩 в коде, который пишется в английской нотации или генерируется автоматически:
Arr.Set(1, 300); - 📖 при чтении чужого кода — явный глагол сразу показывает, что здесь запись, а не чтение;
- 🔁 в паре с
Получить, если в модуле уже принят стиль с методами.
💡 В остальных случаях пишите скобки: короче и привычнее большинству разработчиков 1С.
Если массив пришёл как ФиксированныйМассив (так часто возвращают данные общие модули и параметры сеанса), ни скобки, ни Установить не сработают. Сделайте изменяемую копию:
Изменяемый = Новый Массив(ФиксированныйСписок);
Изменяемый[0] = "Новое значение";
Конструктор Новый Массив(<ФиксированныйМассив>) копирует элементы в обычный массив. Обратное преобразование — Новый ФиксированныйМассив(Изменяемый).
Вставка и удаление элементов массива
Эти операции меняют размер массива и сдвигают индексы. От этого зависит всё остальное: после вставки элемент, который был под индексом 1, оказывается под индексом 2.
Вставить в нужную позицию
Цены = Новый Массив;
Цены.Добавить(100);
Цены.Добавить(250);
Цены.Вставить(0, 50); // 50, 100, 250
Цены.Вставить(2, 175); // 50, 100, 175, 250
Вставка в индекс, равный Количество(), работает как Добавить — элемент встанет в конец.
Удалить по индексу и по значению
Удалить принимает только индекс. Удалить «значение 175» напрямую нельзя: сначала найдите его.
Индекс = Цены.Найти(175);
Если Индекс <> Неопределено Тогда
Цены.Удалить(Индекс);
КонецЕсли;
Найти вернёт только первое вхождение. Чтобы убрать все повторы одного значения, нужен обратный цикл — он в следующем разделе.
Массив нужного размера и заполнение по индексу
Если размер известен заранее, создайте массив конструктором с числом элементов. Все элементы получат значение Неопределено, и их сразу можно менять по индексу, без Добавить:
Остатки = Новый Массив(12); // по месяцам, индексы 0..11
Для Индекс = 0 По Остатки.ВГраница() Цикл
Остатки[Индекс] = 0;
КонецЦикла;
Остатки[2] = 4500; // март
Так удобно готовить «сетку» под данные, которые придут не по порядку: значение за март можно записать раньше, чем за январь. С Добавить пришлось бы следить за последовательностью.
Удалить и Вставить контролируют границы так же, как присваивание: Удалить(5) для массива из трёх элементов вызовет исключение.
Заменить элемент «со сдвигом»
Иногда нужно убрать старый элемент и поставить на его место новый другого смысла. Делать это через Удалить и Вставить незачем — это два сдвига вместо нуля. Достаточно присваивания по индексу.
Как изменить значения массива в цикле
Здесь совершают больше всего ошибок. Поведение зависит от вида цикла и от того, что лежит в массиве.
Цикл «Для Каждого» не меняет примитивы
Для Каждого Цена Из Цены Цикл
Цена = Цена * 2; // массив НЕ изменится
КонецЦикла;
Переменная Цена получает копию значения. Присваивание меняет только переменную, а элемент массива остаётся прежним. Для чисел, строк, дат и булевых это правило работает всегда.
Цикл по индексу меняет элементы
Для Индекс = 0 По Цены.ВГраница() Цикл
Цены[Индекс] = Цены[Индекс] * 2;
КонецЦикла;
Так массив действительно изменится. Если массив пустой, ВГраница() вернёт -1, и цикл не выполнится ни разу — проверка на пустоту не нужна.
Удаление в цикле — только с конца
Прямой цикл с удалением пропускает элементы: после Удалить(2) бывший третий элемент встаёт на место второго, а счётчик уже ушёл дальше. Кроме того, граница цикла Для вычисляется один раз при входе, поэтому в конце счётчик выйдет за пределы уменьшившегося массива.
Правильный вариант — обратный обход:
// Удаляем все нулевые цены
Всего = Цены.Количество();
Для Сч = 1 По Всего Цикл
Индекс = Всего - Сч;
Если Цены[Индекс] = 0 Тогда
Цены.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Проверим на массиве 10, 0, 0, 30 (четыре элемента). Индексы обхода: 3, 2, 1, 0. На индексах 2 и 1 стоят нули — они удаляются. Удаление элемента с индексом 2 не трогает элементы 0 и 1, которые ещё предстоит проверить. Итог: 10, 30.
Если удалять нужно много, часто проще собрать новый массив из подходящих элементов и заменить им старый. Код читается легче, а сдвигов нет.
Отобранные = Новый Массив;
Для Каждого Цена Из Цены Цикл
Если Цена <> 0 Тогда
Отобранные.Добавить(Цена);
КонецЕсли;
КонецЦикла;
Цены = Отобранные;
Вставка в цикле
С вставкой та же проблема, что и с удалением, только наоборот: элементы сдвигаются вправо, и прямой цикл может обработать один и тот же элемент дважды. Используйте обратный обход или сборку нового массива.
Изменение вложенных массивов и структур в массиве
Если в массиве лежат объекты (структуры, другие массивы, соответствия), правила другие. Элемент хранит ссылку на объект, и переменная цикла Для Каждого указывает на тот же объект.
Поля структуры меняются прямо в цикле
Строки = Новый Массив;
Строки.Добавить(Новый Структура("Товар, Цена", "Бумага", 300));
Строки.Добавить(Новый Структура("Товар, Цена", "Ручки", 120));
Для Каждого Строка Из Строки Цикл
Строка.Цена = Строка.Цена + 10; // массив изменится
КонецЦикла;
Здесь Для Каждого работает: мы меняем не переменную, а поле объекта, на который она ссылается. После цикла цены станут 310 и 130.
А вот присваивание всей переменной снова ничего не даст:
Для Каждого Строка Из Строки Цикл
Строка = Новый Структура("Товар, Цена", "Скрепки", 50); // массив НЕ изменится
КонецЦикла;
Чтобы заменить структуру целиком, нужен индекс: Строки[0] = Новый Структура(...).
Присвоить значение несуществующему ключу структуры через точку нельзя: Строка.Скидка = 5 вызовет ошибку Поле объекта не обнаружено. Новый ключ добавляют методом Строка.Вставить("Скидка", 5).
Двумерный массив
Двумерный массив в 1С — это массив массивов. Индексы пишутся подряд:
Матрица = Новый Массив(3, 4); // 3 строки по 4 столбца, все Неопределено
Матрица[1][2] = 15; // строка 1, столбец 2
Матрица[0][0] = Матрица[1][2] * 2;
Каждая строка Матрица[i] — самостоятельный массив. Её можно передать в процедуру и менять там, как обычный одномерный массив.
Общая ссылка: изменили в одном месте — изменилось везде
Исходный = Новый Массив;
Исходный.Добавить(1);
Копия = Исходный;
Копия[0] = 999;
Сообщить(Исходный[0]); // 999
Копия = Исходный не копирует массив, а даёт второе имя тому же объекту. Для настоящей копии создайте новый массив и перенесите элементы в цикле. В конфигурациях на БСП есть функция ОбщегоНазначения.СкопироватьРекурсивно, которая копирует и вложенные структуры.
То же касается параметров процедур. Массив, переданный в процедуру, меняется у вызывающего кода, даже если параметр объявлен с Знач: Знач защищает переменную от замены, но не содержимое объекта.
Ошибки при изменении массива и как их найти
Ниже — проблемы, которые выглядят странно, пока не знаешь причину.
Клиент и сервер
При вызове серверной процедуры с клиента массив сериализуется и передаётся на сервер. Если параметр объявлен без Знач, изменённый массив после вызова вернётся на клиент. С Знач изменения останутся на сервере. Это отличается от обычного вызова внутри одного контекста, где массив передаётся по ссылке.
💡 Надёжнее всего не полагаться на возврат параметров, а делать серверную функцию, которая возвращает новый массив: Цены = ПересчитатьЦеныНаСервере(Цены);. Сразу видно, откуда пришли изменения.
Как посмотреть массив в отладчике
Поставьте точку останова после строки с изменением и откройте окно Вычислить выражение (Shift+F9) или табло. Раскройте переменную массива: видны все индексы и значения. Для массива структур раскрывается каждый элемент. Так быстрее всего проверить, что изменилось и под каким индексом.