Операции над коллекциями

Операции над коллекциями

Существуют одинаковые операции как над изменяемыми, так и над неизменяемыми коллекциями. Общие операции разделяются не следующие группы:

- Трансформация

- Фильтрация

- Сложение и вычитание

- Группировка

- Возвращение части коллекций

- Возвращение одного элемента

- Упорядочивание

- Агрегированные операции

Вышеупомянутые операции возвращают результат без изменения оригинальной коллекции. Например, при фильтрации возвращается новая коллекция с фильтрованным содержимым. Для фильтрации используется функция filter в виде лямбда выражения, куда передаётся условие для фильтрации (поиска):

val items = listOf("one", "two", "three", "four")
// Операция не имеет никакого значения. 
// Результат теряется, так как возвращенный результат не присвоен ни одной переменной
items.filter { it.length > 3 }  
println("numbers are still $items")
// Отфильтровать коллекцию для сохранения результатов, чья длина больше трех элементов
// Результат сохраняется в переменной 'filteredItems'
val filteredItems = items.filter { it.length > 3 } 
// Результат используется для вывода фильтрованных данных
println("numbers longer than 3 chars are $filteredItems")

В некоторых операциях над коллекциями есть возможность определения коллекции назначения. Такая коллекция должна быть изменяемой коллекцией, которой функция добавляет свой результат вместо возвращения новой коллекции. Операции с указанием коллекции назначения существуют отдельные функции, которые заканчиваются словом "To", например filterTo() вместо filter() или associateTo() вместо associate(). Эти функции принимают коллекцию назначения в качестве дополнительного параметра.

val items = listOf("one", "two", "three", "four")
// Коллекция для записи результата фильтрации
val filterResults = mutableListOf<String>()
// Отфильтровать коллекцию для сохранения результатов, чья длина больше трех элементов
items.filterTo(filterResults) { it.length > 3 }
// Отфильтровать элементы по их индексу, вернуть элементы чей индекс равен 0 и добавить в коллекцию filterResults
items.filterIndexedTo(filterResults) { index, _ -> index == 0 }
// Показать результаты применения обоих фильтров
println(filterResults)

Для изменяемых коллекций также доступны операции записи, которые меняют структуру данных коллекции, такие как добавление, удаление и изменение элементов. Для некоторых операций доступны пары функций, один из которых меняет саму коллекцию, а другой их тип возвращает результат как отдельную коллекцию. Например, функция sort() сортирует коллекцию, что меняет порядок элементов коллекции, а функция sorted() создаёт новую коллекцию с теми же элементами, но в сортированном порядке.

val numbers = mutableListOf("one", "two", "three", "four")
// Отсортировать коллекцию и присвоить результат новой переменной
val sortedNumbers = numbers.sorted()
// Так как функция sorted возвращает результат в виде новой коллекции оригинальная коллекция не меняется и она не равна отсортированному результату
println(numbers == sortedNumbers) // false
// Функция sort сортирует коллекцию и меняет порядок элементов
numbers.sort()
// Теперь проверка на равенство даёт положительный результат, так как порядок объектов в оригинальной коллекции отсортирован в таком же порядке как и в переменно sortedNumbers
println(numbers == sortedNumbers) // true

Изменение коллекций

В стандартной библиотеке Kotlin есть набор функций для изменения коллекций. Эти функции создают новые коллекции из существующих на основе предоставленных правил для изменения.

Функция Map

Функция map() применяет ко всем элементам коллекции переданную лямбда функцию и возвращает новую коллекцию с измененными значениями. От этой операции порядок элементов новой коллекции не меняется. Также можно использовать дополнительно индекс элементов применив функцию mapIndexed():

val numbers = setOf(1, 2, 3)
// Вывести результат применения функции map, которая меняет все элементы коллекции умножив их на 3 и возвращает новую коллекцию с новыми значениями
println(numbers.map { it * 3 }) // [3, 6, 9]
// Вывести результат выполнения фукнции mapIndexed результат которого равен новой коллекции, где каждый элемент равен значению оригинальной коллекции умноженный на индекс
println(numbers.mapIndexed { idx, value -> value * idx }) // [0, 2, 6]

В случаях, когда результат применения лямбда выражения может вернуть null, можно отфильтровать null объекты вызвав функцию mapNotNull() вместо функции map или mapIndexedNotNull() вместо функции mapIndexed():

val numbers = setOf(1, 2, 3)
// Если значение элемента коллекции равно 2, присвоить этому элементу значение null, в противном случае умножить на 3
println(numbers.mapNotNull { if ( it == 2) null else it * 3 }) // [3, 9]
// Если значение индекса равно 0, присвоить этому элменту значение null, в противном случае умножить значение на индекс
println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx }) // [2, 6]

При изменении словарей есть два варианта: изменить ключи не затронув значения или изменить значения не затронув ключи. Для изменения ключей используется функция mapKeys(), для изменения значений mapValues(). Обе функции получают элемент словаря, что делает доступным и ключ и значение элемента:

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
// Перевести значение ключей словаря на ВЕРХНИЙ РЕГИСТР
println(numbersMap.mapKeys { it.key.toUpperCase() }) // {KEY1=1, KEY2=2, KEY3=3, KEY11=11}
// Изменить значения путём сложения значения элемента с длиной его ключа
println(numbersMap.mapValues { it.value + it.key.length }) // {key1=5, key2=6, key3=7, key11=16}