Массив в 1С — упорядоченная коллекция значений, к элементам которой обращаются по номеру, то есть по индексу. Это самая простая и самая частая коллекция встроенного языка. Ниже — все методы массива в одной таблице, получение элемента по индексу, обход и удаление, передача между клиентом и сервером, готовый общий модуль и ошибки, на которых спотыкаются чаще всего.
Массив доступен и на клиенте, и на сервере, примеры работают на платформе 8.3. Исключение — функции СтрРазделить и СтрСоединить: они есть в платформе с версии 8.3.6 и работают, только если режим совместимости конфигурации не ниже 8.3.6. В старых конфигурациях используйте СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок из БСП или цикл с функциями Найти и Сред (СтрНайти тоже появилась только в 8.3.6).
Элементы.Список.ВыделенныеСтроки — массив выделенных строк таблицы формы. В списке справочника или документа это ссылки.ТаблицаЗначений.ВыгрузитьКолонку() возвращает массив значений колонки, НайтиСтроки() — массив найденных строк.СтрРазделить() разбивает строку на массив подстрок: артикулы через точку с запятой, колонки строки CSV.В (&Список) чаще всего передают массивом.Если к данным нужен доступ не по номеру, а по ключу, возьмите соответствие или структуру. Если у каждой записи несколько полей — таблицу значений.
Конструктор Новый Массив вызывают без параметров, с количеством элементов или с фиксированным массивом. Число в параметре создаёт массив заданного размера, заполненный значениями Неопределено. Несколько чисел дают многомерный массив — массив, элементы которого тоже массивы.
// Пустой массив — самый частый вариант
Товары = Новый Массив;
// Три элемента, каждый равен Неопределено
Коды = Новый Массив(3);
Коды[0] = "К-01";
// Двумерный массив 2 x 3: два вложенных массива по три элемента
Матрица = Новый Массив(2, 3);
Матрица[1][2] = 150;
// Из строки с разделителем (8.3.6+, режим совместимости не ниже 8.3.6)
Артикулы = СтрРазделить("А-100;А-200;;А-300", ";", Ложь); // 3 элемента, пустой пропущен
// Изменяемый массив из фиксированного
Фиксированный = Новый ФиксированныйМассив(Артикулы);
Изменяемый = Новый Массив(Фиксированный);
Изменяемый.Добавить("А-400");
Массив в 1С не типизирован: в одном массиве могут лежать строка, число и ссылка. Но код, который обходит такой массив, придётся обвешивать проверками ТипЗнч, поэтому держите в одном массиве значения одного типа.
| Метод | Что делает | Что возвращает | Если индекса нет |
|---|---|---|---|
Добавить(Значение) | Добавляет элемент в конец | — | — |
Вставить(Индекс, Значение) | Вставляет элемент, остальные сдвигаются вправо | — | Массив дополняется значениями Неопределено до нужного индекса |
Получить(Индекс) или [Индекс] | Читает элемент | Значение элемента | Ошибка |
Установить(Индекс, Значение) или [Индекс] = | Заменяет элемент | — | Ошибка |
Найти(Значение) | Ищет первое вхождение значения | Индекс или Неопределено | — |
Удалить(Индекс) | Удаляет элемент, остальные сдвигаются влево | — | Ошибка |
Количество() | Считает элементы | Число, у пустого массива 0 | — |
ВГраница() | Даёт индекс последнего элемента | Число, у пустого массива −1 | — |
Очистить() | Удаляет все элементы | — | — |
Пример, в котором задействованы все методы: добавить в массив два склада, вставить третий между ними, прочитать, заменить, найти и удалить. Процедура оформлена как обработчик команды формы, но тот же код без изменений выполнится и на сервере.
&НаКлиенте
Процедура ПоказатьМетодыМассива(Команда)
Склады = Новый Массив;
Склады.Добавить("Основной"); // [Основной]
Склады.Добавить("Розница"); // [Основной, Розница]
Склады.Вставить(1, "Транзит"); // [Основной, Транзит, Розница]
Сообщить(Склады[0] + " / " + Склады.Получить(1)); // Основной / Транзит
Склады.Установить(2, "Магазин"); // [Основной, Транзит, Магазин]
Склады[0] = "Центральный"; // то же, что Установить(0, ...)
Индекс = Склады.Найти("Транзит"); // 1, если не найдено — Неопределено
Если Индекс <> Неопределено Тогда
Склады.Удалить(Индекс); // [Центральный, Магазин]
КонецЕсли;
Сообщить("Элементов: " + Склады.Количество()
+ ", индекс последнего: " + Склады.ВГраница()); // 2 и 1
Склады.Очистить(); // Количество() = 0, ВГраница() = -1
КонецПроцедуры
Индексы начинаются с нуля. В массиве из трёх элементов есть индексы 0, 1 и 2, а обращение к элементу с индексом 3 заканчивается ошибкой «Индекс находится за границами массива». Запись Массив[Индекс] и вызов Массив.Получить(Индекс) равнозначны, так же как присваивание Массив[Индекс] = Значение и Массив.Установить(Индекс, Значение). Скобки короче, поэтому в типовых конфигурациях встречаются чаще.
Путаницу чаще всего вызывают два «размера». Количество() — сколько элементов в массиве, ВГраница() — индекс последнего из них, на единицу меньше. У пустого массива количество равно 0, а верхняя граница равна −1. Отсюда правило для цикла по индексу: от 0 до ВГраница().
// Индексы от 0 до ВГраница()
Для Индекс = 0 По Артикулы.ВГраница() Цикл
Сообщить(Строка(Индекс) + ": " + Артикулы[Индекс]);
КонецЦикла;
// Так нельзя: на последнем шаге индекс равен Количество() и выходит за границу
// Для Индекс = 0 По Артикулы.Количество() Цикл
// Индекс не нужен — Для Каждого
Для Каждого Артикул Из Артикулы Цикл
Сообщить(Артикул);
КонецЦикла;
// Последний элемент — только для непустого массива
Если Артикулы.Количество() > 0 Тогда
Последний = Артикулы[Артикулы.ВГраница()];
КонецЕсли;
Цикл Для Индекс = 0 По Артикулы.ВГраница() по пустому массиву не выполнится ни разу: конечное значение −1 меньше начального, поэтому отдельная проверка на пустоту не нужна. А вот перед обращением к конкретному элементу, например к первому или последнему, проверять количество обязательно.
Для чтения удобнее всего Для Каждого: не нужно думать об индексах. Но переменная цикла получает значение элемента, а не связь с ним. Присваивание ей нового значения массив не меняет. Чтобы заменить элементы, нужен цикл по индексу и запись Массив[Индекс] = ....
Удалять элементы при прямом обходе нельзя. После Удалить(Индекс) все следующие элементы сдвигаются на одну позицию влево, и тот, что встал на место удалённого, цикл пропустит. В цикле Для ... По к этому добавляется ошибка выхода за границу: конечное значение вычисляется один раз до начала цикла, а массив тем временем укоротился. Правильно — идти с конца:
// Удаляем незаполненные элементы, двигаясь с конца массива
Индекс = Артикулы.ВГраница();
Пока Индекс >= 0 Цикл
Если Не ЗначениеЗаполнено(Артикулы[Индекс]) Тогда
Артикулы.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
При обходе с конца сдвигаются только уже проверенные элементы, поэтому ничего не теряется и индекс не выходит за границы.
Массив можно передать с клиента на сервер и обратно, если все его элементы тоже передаются между клиентом и сервером: примитивные типы, ссылки, структуры, соответствия, вложенные массивы. Таблицу значений или объект справочника внутри массива на клиент не передать. Типичный сценарий — пользователь выделил строки в списке, клиент отправляет массив ссылок на сервер, сервер отвечает массивом строк:
// Модуль формы списка справочника Номенклатура
&НаКлиенте
Процедура ПоказатьНаименования(Команда)
Выделенные = Элементы.Список.ВыделенныеСтроки; // Массив ссылок
Если Выделенные.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Наименования = НаименованияПоСсылкам(Выделенные);
Сообщить(СтрСоединить(Наименования, ", "));
КонецПроцедуры
&НаСервереБезКонтекста
Функция НаименованияПоСсылкам(Знач Ссылки)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&Ссылки)
|
|УПОРЯДОЧИТЬ ПО
| Наименование";
Запрос.УстановитьПараметр("Ссылки", Ссылки);
// ВыгрузитьКолонку возвращает Массив строк — его можно вернуть на клиент
Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Наименование");
КонецФункции
Параметр серверной функции объявлен как Знач. Без этого слова платформа после вызова вернула бы массив обратно на клиент — это лишний трафик. Обратная сторона: если серверная процедура изменит массив, клиент этих изменений не увидит, поэтому результат возвращайте из функции явно.
Внутри одного контекста всё иначе. Массив — объект, и в процедуру передаётся ссылка на него. Даже с Знач вызов Параметр.Добавить() изменит массив вызывающего кода. Знач защищает только от замены самой переменной: присваивание Параметр = Новый Массив внутри процедуры снаружи не видно.
У массива нет методов сортировки, удаления дублей, копирования и сравнения — их приходится писать самому. Создайте общий модуль РаботаСМассивамиКлиентСервер, включите флажки «Клиент (управляемое приложение)», «Сервер» и «Внешнее соединение» и вставьте код. Вызов из любого места: РаботаСМассивамиКлиентСервер.УникальныеЗначения(Артикулы).
// Общий модуль РаботаСМассивамиКлиентСервер
// Флажки: Клиент (управляемое приложение), Сервер, Внешнее соединение.
// Директивы компиляции не нужны: функции доступны везде, где скомпилирован модуль.
// Возвращает элемент по индексу или ЗначениеПоУмолчанию, если индекса в массиве нет.
Функция ЭлементМассива(Массив, Индекс, ЗначениеПоУмолчанию = Неопределено) Экспорт
Если ТипЗнч(Индекс) <> Тип("Число")
Или Индекс < 0
Или Индекс <> Цел(Индекс)
Или Индекс > Массив.ВГраница() Тогда
Возврат ЗначениеПоУмолчанию;
КонецЕсли;
Возврат Массив[Индекс];
КонецФункции
// Удаляет все вхождения значения. Возвращает количество удалённых элементов.
Функция УдалитьВсеВхождения(Массив, Значение) Экспорт
Удалено = 0;
Индекс = Массив.ВГраница();
Пока Индекс >= 0 Цикл
Если Массив[Индекс] = Значение Тогда
Массив.Удалить(Индекс);
Удалено = Удалено + 1;
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Возврат Удалено;
КонецФункции
// Возвращает новый массив без повторов, порядок первых вхождений сохраняется.
Функция УникальныеЗначения(Массив) Экспорт
Результат = Новый Массив;
Встречались = Новый Соответствие;
Для Каждого Элемент Из Массив Цикл
Если Встречались[Элемент] = Неопределено Тогда
Встречались.Вставить(Элемент, Истина);
Результат.Добавить(Элемент);
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
// Поверхностная копия: новый массив с теми же элементами.
Функция КопияМассива(Массив) Экспорт
Копия = Новый Массив;
Для Каждого Элемент Из Массив Цикл
Копия.Добавить(Элемент);
КонецЦикла;
Возврат Копия;
КонецФункции
// Сравнивает массивы поэлементно с учётом порядка.
Функция МассивыРавны(Массив1, Массив2) Экспорт
Если Массив1.Количество() <> Массив2.Количество() Тогда
Возврат Ложь;
КонецЕсли;
Для Индекс = 0 По Массив1.ВГраница() Цикл
Если Массив1[Индекс] <> Массив2[Индекс] Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
// Возвращает новый массив, отсортированный по значению (числа, строки, даты).
Функция ОтсортированныйМассив(Массив, ПоУбыванию = Ложь) Экспорт
Список = Новый СписокЗначений;
Список.ЗагрузитьЗначения(Массив);
Если ПоУбыванию Тогда
Список.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
Иначе
Список.СортироватьПоЗначению(НаправлениеСортировки.Возр);
КонецЕсли;
Возврат Список.ВыгрузитьЗначения();
КонецФункции
Цена = РаботаСМассивамиКлиентСервер.ЭлементМассива(Колонки, 3, "0"). Условия в Если вычисляются слева направо и до первого истинного, поэтому сравнение с нулём не выполнится, если индекс не число.Найти() в цикле тоже можно, но каждый вызов перебирает массив с начала.Массив.Найти() перебирает элементы подряд. На массиве в десятки тысяч элементов вариант с Найти() для каждого элемента заметно медленнее.= для двух разных массивов с одинаковым содержимым вернёт Ложь: он сравнивает сами объекты, а не их содержимое.Количество() вместо ВГраница(), обращение к [0] в пустом массиве, счёт индексов с единицы, удаление элементов в прямом цикле.Новый Массив(3) уже содержит три элемента, и Добавить() допишет четвёртый. Если размер заранее не нужен, создавайте пустой массив. Если нужен — заполняйте элементы через [Индекс], а не через Добавить().ФиксированныйМассив: так часто возвращают данные параметры сеанса и кэширующие функции типовых конфигураций. Фиксированный массив только читается. Сделайте изменяемую копию: Новый Массив(ФиксированныйМассив).Элемент = СокрЛП(Элемент) внутри Для Каждого ничего не меняет в массиве. Нужен цикл по индексу.Найти() ищет тот же самый объект, а не объект с таким же содержимым. Две структуры с одинаковыми полями — два разных значения.Знач при серверном вызове на клиент не возвращается. Возвращайте массив результатом функции.Сравнить Массив.Количество() с нулём. Если в переменной может оказаться не массив, а, например, Неопределено из необязательного параметра, сначала проверьте тип: ТипЗнч(Значение) = Тип("Массив").
Выражением Массив[Массив.ВГраница()] — после проверки, что массив не пустой. У пустого массива верхняя граница равна −1, и обращение закончится ошибкой.
Функцией УникальныеЗначения из модуля выше. В конфигурациях на БСП есть готовая функция ОбщегоНазначенияКлиентСервер.СвернутьМассив(Массив), которая возвращает новый массив без повторов. Её наличие и параметры сверьте с версией библиотеки в вашей конфигурации.
Циклом Для Каждого по второму массиву с вызовом Добавить() для первого. В БСП для этого есть процедура ОбщегоНазначенияКлиентСервер.ДополнитьМассив: она дописывает элементы одного массива в другой, а третьим параметром можно попросить добавить только значения, которых в приёмнике ещё нет.
СтрСоединить(Массив, ",") склеивает элементы через разделитель, СтрРазделить(Строка, ",") разбивает строку обратно. Используйте один и тот же разделитель в обе стороны. Второй параметр СтрРазделить — не подстрока, а набор символов: при ", " строка режется и по запятой, и по каждому пробелу внутри значений. Если строку склеили через ", ", разбейте её по "," и обрежьте пробелы:
Части = СтрРазделить(Строка, ",", Ложь);
Для Индекс = 0 По Части.ВГраница() Цикл
Части[Индекс] = СокрЛП(Части[Индекс]);
КонецЦикла;
Третий параметр Ложь отбрасывает пустые подстроки, поэтому пустые элементы исходного массива при обратном преобразовании пропадут. Если запятая встречается в самих значениях, склеивайте через символ, которого в данных нет, например Символы.Таб. СтрСоединить рассчитана на массив строк: числа, даты и ссылки заранее преобразуйте функцией Строка() или Формат().
Массивы — строительный материал почти любой обработки: выгрузок, загрузок, групповой перепроводки и заполнения документов. Если нужна готовая обработка или отчёт под вашу задачу, а не разбор синтаксиса, посмотрите услугу отчёты и обработки 1С или разместите задачу на площадке: оценка бесплатная, ответ — в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.