# Коллекции и структуры данных
В этом разделе описаны основные типы коллекций в 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}; // объявление словаря
```
```{attention}
Если в карте добавляется несколько элементов с одинаковыми ключами, то последнее значение перезапишет предыдущее. В словаре все ключи уникальны, и каждому ключу соответствует только одно значение.
```
```
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)]`

Примеры использования  
```jexl
//Пример создания 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
- каждый элемент коллекции — кортеж ("имя", значение).

Кортежи неизменяемы, что гарантирует:
- параметры не поменяются случайно во время выполнения процедуры;
- нет риска коллизий в многопоточных сценариях.