Замена, вставка и удаление элементов массива во встроенном языке 1С:Предприятия 8.3

· 9 мин

Содержание

Чтобы изменить значение в массиве 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) или табло. Раскройте переменную массива: видны все индексы и значения. Для массива структур раскрывается каждый элемент. Так быстрее всего проверить, что изменилось и под каким индексом.

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

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