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

· 9 мин

Содержание

Чтобы изменить значение в массиве 1С, присвойте новое значение элементу по индексу: Массив[2] = "Новое";. То же самое делает метод Массив.Установить(2, "Новое"). Нумерация идёт с нуля, поэтому [2] — это третий элемент.

Частая ошибка новичка — менять элемент через переменную цикла Для Каждого. Код Элемент = 0; внутри такого цикла массив не меняет: перезаписывается только переменная. Ниже — рабочие способы для простых значений, структур и замены по условию. Всё описанное относится к встроенному языку платформы 8.3 и не зависит от конфигурации.

Присваивание по индексу и метод Установить

У массива два равноценных способа записать значение в существующую ячейку.

Цвета = Новый Массив;
Цвета.Добавить("красный");   // индекс 0
Цвета.Добавить("зелёный");   // индекс 1
Цвета.Добавить("синий");     // индекс 2

// Способ 1: квадратные скобки
Цвета[1] = "жёлтый";

// Способ 2: метод Установить(Индекс, Значение)
Цвета.Установить(2, "фиолетовый");

// Результат: красный, жёлтый, фиолетовый

Квадратные скобки короче и читаются привычнее. Установить() удобен, когда индекс и значение вычисляются в одном выражении или когда хочется явно показать, что ячейка перезаписывается. Для чтения парой к нему идёт Получить(Индекс).

Что нужно помнить про индексы:

  • 🔢 первый элемент — [0], последний — [Массив.ВГраница()];
  • 📏 ВГраница() всегда равна Количество() - 1, у пустого массива это -1;
  • 🚫 присваивание работает только в существующую ячейку. Записать в [Массив.Количество()] нельзя — для новых элементов есть Добавить() и Вставить().

Тип значения в ячейке можно менять свободно. Массив в 1С не типизирован: в ячейке, где было число, может оказаться строка, ссылка или Неопределено.

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

Конструктор с несколькими параметрами создаёт массив массивов. Индексы указываются подряд:

Сетка = Новый Массив(3, 4);   // 3 строки по 4 ячейки, все Неопределено
Сетка[0][3] = 100;            // первая строка, четвёртая ячейка
Сетка[2][0] = "итог";

Каждый вложенный массив — обычный Массив, поэтому к нему применимы те же методы: Сетка[1].Установить(2, 5).

Как изменить элементы массива в цикле

Здесь кроется главная ловушка. Сравните два фрагмента:

Цены = Новый Массив;
Цены.Добавить(100);
Цены.Добавить(250);
Цены.Добавить(80);

// НЕ работает: меняется только переменная Цена
Для Каждого Цена Из Цены Цикл
    Цена = Цена * 1.2;
КонецЦикла;
// Цены по-прежнему 100, 250, 80

// Работает: обращение к ячейке по индексу
Для Индекс = 0 По Цены.ВГраница() Цикл
    Цены[Индекс] = Цены[Индекс] * 1.2;
КонецЦикла;
// Цены: 120, 300, 96

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

Пересчитаем пример: 100 × 1,2 = 120; 250 × 1,2 = 300; 80 × 1,2 = 96. Результат совпадает с комментарием.

Когда массив пустой

Цикл Для Индекс = 0 По Цены.ВГраница() на пустом массиве не выполнится ни разу: граница равна -1, и начальное значение 0 уже больше конечного. Отдельная проверка Если Цены.Количество() > 0 для такого цикла не нужна.

Удаление вместо замены

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

Всего = Цены.Количество();
Для Сч = 1 По Всего Цикл
    Индекс = Всего - Сч;
    Если Цены[Индекс] < 100 Тогда
        Цены.Удалить(Индекс);
    КонецЕсли;
КонецЦикла;

Граница Всего посчитана до цикла, а индекс берётся от этого числа, а не от меняющегося Количество(). При обходе с конца удаление не сдвигает ещё не проверенные элементы.

Изменение значений в массиве структур

С массивом структур всё наоборот: Для Каждого подходит. Структура — объект, и переменная цикла указывает на тот же объект, что лежит в массиве. Меняя поле, вы меняете элемент.

Товары = Новый Массив;
Товары.Добавить(Новый Структура("Наименование, Количество, Цена", "Бумага А4", 10, 350));
Товары.Добавить(Новый Структура("Наименование, Количество, Цена", "Скрепки", 50, 40));

// Поле меняется прямо в элементе массива
Для Каждого Товар Из Товары Цикл
    Товар.Цена = Товар.Цена + 10;
КонецЦикла;
// Цены: 360 и 50

// То же по индексу
Товары[1].Количество = 45;

Три операции, которые путают между собой:

Что пишемЧто произойдёт
Товар.Цена = 500изменится поле в элементе массива
Товар.Вставить("Цена", 500)изменит поле, а если его не было — добавит
Товар = Новый Структура(...)изменится только переменная, в массиве останется старая структура

Чтобы заменить структуру целиком, присвойте её ячейке: Товары[0] = Новый Структура("Наименование, Количество, Цена", "Бумага А3", 5, 700);.

Обращение к полю, которого нет

Присваивание Товар.Скидка = 5 упадёт с ошибкой Поле объекта не обнаружено (Скидка), если такого ключа в структуре нет. Варианты:

  • ✅ Товар.Вставить("Скидка", 5) — создаст ключ или перезапишет существующий;
  • ✅ Если Товар.Свойство("Скидка") Тогда ... КонецЕсли — сначала проверить, потом менять.

Если в массиве разнородные структуры (часть собрана из одного места, часть из другого), Вставить() надёжнее прямого присваивания.

Поиск элемента и замена по условию

Замена первого найденного значения

Метод Найти() возвращает индекс первого совпадения или Неопределено:

Индекс = Цвета.Найти("жёлтый");
Если Индекс <> Неопределено Тогда
    Цвета[Индекс] = "оранжевый";
КонецЕсли;

Проверка на Неопределено обязательна. Без неё строка Цвета[Неопределено] = ... вызовет ошибку выполнения.

Замена всех вхождений

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

Заменено = 0;
Для Индекс = 0 По Цвета.ВГраница() Цикл
    Если Цвета[Индекс] = "жёлтый" Тогда
        Цвета[Индекс] = "оранжевый";
        Заменено = Заменено + 1;
    КонецЕсли;
КонецЦикла;
Сообщить(СтрШаблон("Заменено элементов: %1", Заменено));

СтрШаблон сам приводит число к строке. При склейке через + используйте Формат(Заменено, "ЧГ=0"), иначе на больших числах появится разделитель групп разрядов.

Замена пустых значений на значение по умолчанию

Типичная практическая задача: массив собран из результата запроса, и часть ячеек пустая. Перед расчётом их заменяют нулём:

Для Индекс = 0 По Суммы.ВГраница() Цикл
    Если Суммы[Индекс] = Null Или Суммы[Индекс] = Неопределено Тогда
        Суммы[Индекс] = 0;
    КонецЕсли;
КонецЦикла;

Если в ячейках ожидаются только числа, удобнее проверить тип: Если ТипЗнч(Суммы[Индекс]) <> Тип("Число") Тогда. Такое условие поймает и Null, и Неопределено, и случайно попавшую строку.

Поиск в массиве структур по значению поля

Найти() сравнивает структуры как объекты, а не по содержимому. Он найдёт только ту же самую структуру, а не другую с теми же полями. Поэтому ищем по полю вручную:

Для Каждого Товар Из Товары Цикл
    Если Товар.Наименование = "Скрепки" Тогда
        Товар.Цена = 45;
        Прервать;   // уберите, если нужно обновить все совпадения
    КонецЕсли;
КонецЦикла;

Если поиск по полю нужен постоянно и элементов тысячи, соберите рядом Соответствие «ключ → структура». Поиск по ключу в соответствии не требует перебора, а меняются поля всё той же структуры из массива.

ПоНаименованию = Новый Соответствие;
Для Каждого Товар Из Товары Цикл
    ПоНаименованию.Вставить(Товар.Наименование, Товар);
КонецЦикла;

Найденный = ПоНаименованию.Получить("Бумага А4");
Если Найденный <> Неопределено Тогда
    Найденный.Количество = 12;   // изменится и элемент массива
КонецЕсли;

Ссылки, копии и неизменяемые массивы

Многие «странные» эффекты при замене значений объясняются тем, что массив — объект, а переменная хранит ссылку на него.

Присваивание не копирует массив

Исходный = Новый Массив;
Исходный.Добавить(1);
Копия = Исходный;      // это та же коллекция
Копия[0] = 99;
// Исходный[0] тоже равен 99

Чтобы изменить копию и не тронуть оригинал, создайте новый массив. Для простых значений хватит такого кода:

Копия = Новый Массив;
Для Каждого Значение Из Исходный Цикл
    Копия.Добавить(Значение);
КонецЦикла;

Для массива структур этого мало: в новом массиве окажутся те же структуры, и правка поля отразится в обоих. Нужна глубокая копия. В типовых конфигурациях на БСП есть функция ОбщегоНазначения.СкопироватьРекурсивно(), она копирует вложенные массивы и структуры.

Параметр Знач не спасает от изменений

Процедура Пересчитать(Знач Цены) защищает только переменную: присвоить ей новый массив внутри процедуры можно без последствий для вызывающего кода. Но Цены[0] = 0 внутри процедуры изменит исходный массив, потому что копируется ссылка, а не сама коллекция. Если процедура не должна портить данные, передайте ей копию.

ФиксированныйМассив

ФиксированныйМассив создаётся из обычного массива и запрещает изменения: у него нет методов Установить, Добавить, Удалить, присвоить значение ячейке тоже нельзя. Такие массивы часто приходят из повторно используемых модулей и параметров сеанса.

Чтобы поменять значение, сделайте изменяемую копию:

Изменяемый = Новый Массив(ФиксированныйСписок);
Изменяемый[0] = "новое значение";

Конструктор Новый Массив(<ФиксированныйМассив>) возвращает обычный массив с теми же элементами. Исходный фиксированный массив останется прежним.

Ошибки при замене значений и граничные случаи

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

Ещё несколько граничных случаев, на которых спотыкаются:

  1. Индекс — дробное число. Если индекс получен вычислением, например делением, округлите его явно: Цел(Позиция / 2). Не полагайтесь на то, что платформа сама отбросит дробную часть.
  2. Индекс пришёл из Найти(). Без проверки на Неопределено код упадёт, как только значения в массиве не окажется.
  3. Замена во время перебора Для Каждого. Запись Цены[Индекс] = ... внутри такого цикла технически возможна, если вести счётчик отдельно, но читается плохо. Если меняете ячейки — сразу берите цикл по индексу.
  4. Сравнение с Неопределено и Null. Значения из запроса могут прийти как Null, а «пустые» ячейки нового массива — Неопределено. При замене пустых значений проверяйте оба варианта.

Какой способ изменения массива выбрать

ЗадачаСпособНа что смотреть
Заменить элемент, индекс известенМассив[i] = Значение или Установить(i, Значение)индекс от 0 до ВГраница()
Изменить все простые значенияцикл Для Индекс = 0 По ВГраница()не Для Каждого
Изменить поле у всех структурДля Каждого + Элемент.Поле = ...ключ должен существовать
Заменить первое совпадениеНайти() + проверка на Неопределеноищет только первое
Заменить все совпаденияцикл по индексу с условиемсчётчик замен для контроля
Найти структуру по полюперебор или Соответствие по ключуНайти() по содержимому не ищет
Удалить элементы по условиюобратный цикл с Удалить()границу считать до цикла
Изменить фиксированный массивНовый Массив(Фиксированный)меняется копия

Если массив растёт до десятков тысяч строк с несколькими полями, а замены идут по условиям, подумайте о ТаблицаЗначений. У неё есть НайтиСтроки() с отбором по нескольким колонкам и индексы для быстрого поиска. Колонку таблицы можно выгрузить в массив методом ВыгрузитьКолонку() и загрузить обратно через ЗагрузитьКолонку().

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

Как заменить значение в массиве, если индекс неизвестен?
Найдите индекс методом Найти(Значение), проверьте, что результат не Неопределено, и присвойте новое значение ячейке с этим индексом.
Можно ли вставить новое значение между элементами, а не заменить?
Да, методом Вставить(Индекс, Значение). Элементы начиная с этого индекса сдвинутся на одну позицию вправо, количество вырастет на один.
Чем Установить() отличается от присваивания через квадратные скобки?
По результату ничем: оба записывают значение в существующую ячейку и оба выдают ошибку при индексе за границами. Выбор — дело стиля.
Есть ли метод, который заменит сразу все одинаковые значения?
Нет, у массива такого метода нет. Пройдите по индексам циклом и замените подходящие элементы.
Как поменять местами два элемента массива?
Через временную переменную: Врем = М[0]; М[0] = М[1]; М[1] = Врем;.