Перейти к основному содержанию
`Ctrl`+`K`

# Коллекции и структуры данных

В этом разделе описаны основные типы коллекций в JEXL: списки, массивы, множества, карты и кортежи. Вы узнаете, как объявлять коллекции, добавлять и удалять элементы, а также выполнять основные операции для работы с данными. Материал содержит практические примеры для каждого типа коллекций.

## Список

Список является изменяемым типом данных (mutable). Это значит, что можно добавлять и удалять элементы, а также изменять существующие.

Исходный массив для примера:

    var mvStock = ["P-1001", "P-1002", "P-1003", ...];

Основные операции

Объявление списков:

    var mvStockApproachOne = [...];

Добавление элементов (только для mutable):

    mvStock.add("P-1004");

Получение элементов:

    mvStock[0]; // способ 1
    mvStock.get(0); // способ 2

Проверка наличия элемента:

    mvStock.contains("P-1002"); // возвращает true/false

Удаление элементов (только для mutable):

    mvStock.remove("P-1002");

Размер списка:

    mvStock.size(); // возвращает количество элементов

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

    mvStock.isEmpty(); // возвращает true/false

Поиск индекса элемента:

    mvStock.indexOf("P-1001"); // возвращает индекс или -1

Замена элемента (только для mutable):

    mvStock.set(0, "newElement"); // способ 1
    mvStock[0] = "newElement"; // способ 2

Объединение списков (только для mutable):

    var listUnion = [1,2,3, ...];
    mvStockApproachOne.addAll(listUnion);

Очистка списка (только для mutable):

    mvStock.clear(); // удаляет все элементы

## Массив

Массив — это изменяемая (mutable) структура данных с фиксированным размером. Вы не можете добавлять и удалять элементы, но можно изменять значения существующих элементов (если они сами по себе изменяемы).

Исходный массив для примера:

    var mvStock = ["P-1001", "P-1002", "P-1003"];

Основные операции

Объявление массива:

    var mvStockApproachOne = [];
    var mvStockApproachTwo = [1, 2, 3];

Получение элементов:

    mvStock[0]; // способ 1
    mvStock.get(0); // способ 2

Обновление значений:

    mvStock[0] = 100;

Проверка наличия элемента:

    mvStock.contains("P-1002"); // возвращает true/false

Размер списка:

    mvStock.size(); // количество элементов

Поиск индекса элемента:

    mvStock.indexOf("P-1001"); // возвращает индекс или -1

## Множество

Доступны только mutable (изменяемые) множества. Основные операции

Создание множества:

    var setData = {"P-1001", "P-1002", "P-1003"}; // Множество с элементами
    var setEmptyData = {}; // Пустое множество

Добавление элементов:

    setData.add("P-1004"); // Добавление одного элемента
    setData.addAll({"P-1005", "P-1006"}); // Добавление другого множества
    setData.addAll(["P-1007", "P-1008"]); // Добавление элементов из списка

При добавлении списка порядок элементов множества сбивается.

Проверка и получение элементов:

    var exists = setData.contains("P-1001"); // Проверка наличия → true/false

Удаление элементов:

    setData.remove("P-1001"); // Удаление по значению
    setData.clear(); // Полная очистка множества

Работа с размерами:

    var size = setData.size(); // Количество элементов
    var isEmpty = setData.isEmpty(); // Проверка на пустоту → true/false

Операции с множествами:

    // Пересечение (оставляет только общие элементы)
    setData.retainAll({"P-1005", "P-1006"});

    // Разность (удаляет элементы другого множества)
    setData.removeAll({"P-1002", "P-1003"});

## Карта

Далее в примерах будем работать с этой картой:

    var mapData = {"bwhasError" : 11, "svKeyTransaction" : 21}; // объявление словаря

Внимание

Если в карте добавляется несколько элементов с одинаковыми ключами, то последнее значение перезапишет предыдущее. В словаре все ключи уникальны, и каждому ключу соответствует только одно значение.

    var mapData = {
        "bwhasError": 11,
        "svKeyTransaction": 21, // Это значение будет перезаписано
        "svKeyTransaction": 22 // Останется только это
    };
    // Итоговая карта: {"bwhasError": 11, "svKeyTransaction": 22}

Основные операции со словарями

Получение и добавление элемента:

    mapData.e = 5; // добавление нового ключа (способ 1)
    mapData.put("c", 3); // добавление нового ключа (способ 2)
    mapData["d"] = 4; // добавление нового ключа (способ 3)

    var approachTwo = mapData.bwhasError; // получение значения по ключу (способ 1)
    var approachOne = mapData.get("bwhasError"); // получение значения по ключу (способ 2)
    var approachThree = mapData["bwhasError"]; // получение значения по ключу (способ 3)

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

- Точечная нотация работает только с ключами, которые являются валидными идентификаторами (без пробелов, дефисов, начинающиеся с буквы).

- Для ключей со спецсимволами (тире, пробелы, точки) используйте квадратные скобки.

- Методы put()/get() работают всегда, но требуют больше кода.

Пример с ограничениями:

    var mvStock = {"P-1001" : 1, "P-1002" : 2, "P-1003" : 3};
    var nCode = mvStock.P-1001; // Дефис воспримется как оператор

Проверка наличия ключа:

    mapData.containsKey("bwhasError"); // возвращает true/false

Удаление элемента:

    mapData.remove("bwhasError"); // удаляет пару "ключ-значение" по ключу

Размер словаря:

    mapData.size(); // количество пар "ключ-значение"

Получение всех ключей:

    mapData.keySet(); // возвращает массив ключей (например,["bwhasError", "svKeyTransaction"])

Получение всех значений:

    mapData.values(); // возвращает массив значений (например, [11, 21])

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

    mapData.isEmpty(); // true, если карта пустая

Изменение значения по ключу:

    mapData["bwhasError"] = 10; // присваивает новое значение ключу

Добавление новой пары «ключ-значение»:

    mapData["newKey"] = 42; // добавляет новый ключ с значением

Очистка словаря:

    mapData.clear(); // удаляет все элементы

Объединение словарей:

    var newMap = {"key1": 1, "key2": 2};
    mapData.addAll(newMap); // добавляет все пары из newMap в mapData (существующие ключи перезаписываются)

Операция entrySet:

    var mapData = {"bwhasError" : 11, "svKeyTransaction" : 21};
    var setMap = mapData.entrySet(); // Получаем множество пар ключ-значение

    for (entry : setMap) { // Итерация по всем элементам (парам ключ-значение)
        var key = entry.getKey(); // Получить ключ текущей пары
        var value = entry.getValue(); // Получить значение текущей пары
    }

Возвращает множество(Set) пар ключ-значение - объектов Map.Entry.

Каждый Entry содержит:

- getKey() - уникальный ключ;

- getValue() - соответствующее значение.

Перебор значений идет в обратном порядке.

Когда использовать entrySet():

- Когда нужны и ключи, и значения в цикле.

- Для модификации значений через entry.setValue().

- Для получения «снимка» всех данных Map

Получение ключа по значению

Нет встроенной функции для получения ключа по значению, но можно написать свою локальную функцию для этой цели:

    // Получения ключа по значению
    var findKeysByValue = function(map, targetValue) {
        var matchedKeys = [...]; // Сюда будем складывать подходящие ключи
        for (let key : map.keySet()) { // Перебираем каждый ключ
            if (map[key] == targetValue) { // Если значение по ключу совпадает с искомым
                return key; // Добавляем ключ в результат
            }
        }
        return matchedKeys; // Возвращаем найденные ключи в виде списка
    };

    var keyListForValue = findKeysByValue(mapData, 11);
    dialogs.showMessage(toString(keyListForValue));

По такому же принципу можно реализовать удаление ключа по значению.

## Конвертации коллекций

### Из `Array[?]` / `scala.collection.IterableOnce[?]` / `java.lang.Iterable[?]`

Доступны методы:

- `toArray`, `toIntArray`, `toFloatArray`, `toDoubleArray`, `toLongArray`, `toCharArray`, `toByteArray`, `toShortArray`, `toBooleanArray`
  — конвертация в `Array` и методы, гарантирующие конвертацию в примитивные массивы.

- `toJArrayList`
  — конвертация в `java.util.ArrayList[?]`

- `toSeq`
  — конвертация в `immutable.Seq[?]`

- `toMSeq`
  — конвертация в `mutable.Seq[?]`

- `toList`
  — конвертация в `immutable.List[?]`

- `toSet`
  — конвертация в `immutable.Set[?]`

- `toMSet`
  — конвертация в `mutable.Set[?]`

- `toJHashSet`
  — конвертация в `java.util.HashSet[?]`

- `toTuple`
  — конвертация в `TupleN`

- `toOption`
  — конвертация в `Option[?]`

Если `?` — это `Tuple2[K, V]`:

- `toJHashMap`
  — конвертация в `java.util.HashMap[K, V]`

- `toMap`
  — конвертация в `immutable.Map[K, V]`

- `toMMap`
  — конвертация в `mutable.Map[K, V]`

### Из `java.util.Map[?, ?]` / `immutable.Map[?, ?]` / `mutable.Map[?, ?]`

- `toJHashMap`
  — конвертация в `java.util.HashMap[K, V]`

- `toMap`
  — конвертация в `immutable.Map[K, V]`

- `toMMap`
  — конвертация в `mutable.Map[K, V]`

- `toSeq`
  — конвертация в `immutable.Seq[(K, V)]`

- `toMSeq`
  — конвертация в `mutable.Seq[(K, V)]`

Примеры использования

    //Пример создания Seq (часто используется в Api/Pkg)
    let ropa = [rop1, rop2].toSeq;
    let ida = ropa.map(r => r.id);
    //Создание карты через Tuple
    let map = [["one", 1].toTuple, ["two", 2].toTuple].toMMap;
    //создание Option
    let opt = [null].toOption // None

## Кортеж

Кортежи — это неизменяемые (immutable) структуры данных, которые позволяют хранить фиксированное количество элементов разных типов. Они полезны, когда нужно временно сгруппировать данные без создания отдельного класса.

В JEXL отсутствует встроенный тип данных Кортеж (Tuple), но его можно эмулировать с помощью интеграции с Scala. Кортежи Tuple2 — это естественный способ передать такие данные из JEXL в Scala-код.

    var tuple2 = (v1, v2) -> { return new(`scala.Tuple2`, v1, v2); };
    var value = tuple2("param1", "param2");
    // Или
    var value = ["param1", "param2"].toTuple;

- New(scala.Tuple2, …) — создает экземпляр Scala-кортежа, используя JVM-интеграцию.

- Функция принимает 2 аргумента и возвращает неизменяемую пару (v1, v2).

В Scala стандартные кортежи (Tuple) ограничены 22 элементами (до Tuple22):

    var tuple22 = (v1, v2, v3,v4, v5, v6,v7, v8, v9,v10, v11, v12, v13, v14, v15,v16, v17, v18,v19, v20, v21,v22) -> {
        return new(`scala.Tuple22`, v1, v2, v3,v4, v5, v6,v7, v8, v9,v10, v11, v12, v13, v14, v15,v16, v17, v18,v19, v20, v21,v22);
    };

    dialogs.showMessage(toString(tuple22(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)));

Чтобы передавать параметры в Scala-коллекции, используем asScala() или toSeq:

    // Массив пар [Имя_параметра, Значение]
    var tuple2Params = asScala([
        tuple2("npInParam1", 10l), // Параметр 1 (Long)
        tuple2("npInParam2", 125l), // Параметр 2 (Long)
        tuple2("npInParam3", 1000l), // Параметр 3 (Long)
        ... // Остальные элементы (если есть)
    ]);
    //Или
    var tuple2Params = [
        ["npInParam1", 10l].toTuple, // Параметр 1 (Long)
        ["npInParam2", 125l].toTuple, // Параметр 2 (Long)
        ["npInParam3", 1000l].toTuple, // Параметр 3 (Long)
        ... // Остальные элементы (если есть)
    ].toSeq;

Особенности:

- asScala() — преобразует JEXL-массив в Scala-коллекцию;

- toSeq - преобразует JEXL-массив в неизменяемый Seq

- каждый элемент коллекции — кортеж («имя», значение).

Кортежи неизменяемы, что гарантирует:

- параметры не поменяются случайно во время выполнения процедуры;

- нет риска коллизий в многопоточных сценариях.