Операции преобразования коллекций
Стандартная библиотека Kotlin предоставляет набор расширяющих функций для преобразования коллекций. Эти функции строят новые коллекции из существующих на основе предоставленных правил преобразования. На этой странице мы рассмотрим обзор доступных функций преобразования коллекций.
Отображение
Преобразование отображения создает коллекцию из результатов применения функции к элементам другой коллекции. Основная функция отображения — map(). Она применяет заданную лямбда-функцию к каждому последующему элементу и возвращает список результатов лямбда-функции. Порядок результатов такой же, как и исходный порядок элементов. Для применения преобразования, которое также использует индекс элемента в качестве аргумента, используйте mapIndexed().
fun main() {
//sampleStart
val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })
println(numbers.mapIndexed { idx, value -> value * idx })
//sampleEnd
}
Если преобразование создает null для определенных элементов, вы можете отфильтровать null из результирующей коллекции, вызвав функцию mapNotNull() вместо map(), или mapIndexedNotNull() вместо mapIndexed().
fun main() {
//sampleStart
val numbers = setOf(1, 2, 3)
println(numbers.mapNotNull { if ( it == 2) null else it * 3 })
println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })
//sampleEnd
}
При преобразовании карт у вас есть два варианта: преобразовать ключи, оставив значения неизменными, и наоборот. Для применения заданного преобразования к ключам используйте mapKeys(); в свою очередь, mapValues() преобразует значения. Обе функции используют преобразования, которые принимают пару ключ-значение в качестве аргумента, так что вы можете работать как с ключом, так и со значением.
fun main() {
//sampleStart
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
println(numbersMap.mapKeys { it.key.uppercase() })
println(numbersMap.mapValues { it.value + it.key.length })
//sampleEnd
}
Сжатие
Преобразование сжатия создаёт пары из элементов, имеющих одинаковые позиции в обеих коллекциях. В стандартной библиотеке Kotlin это делается функцией zip().
При вызове для коллекции или массива с другой коллекцией (или массивом) в качестве аргумента, zip() возвращает List объектов Pair. Элементы коллекции-приёмника являются первыми элементами в этих парах.
Если коллекции имеют разную длину, результатом zip() является меньшая длина; последние элементы большей коллекции не включаются в результат.
zip() также можно вызвать в инфиксной форме a zip b.
fun main() {
//sampleStart
val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
println(colors zip animals)
val twoAnimals = listOf("fox", "bear")
println(colors.zip(twoAnimals))
//sampleEnd
}
Вы также можете вызвать zip() с функцией преобразования, которая принимает два параметра: элемент-приёмник и элемент-аргумент. В этом случае, результирующая List содержит значения, возвращаемые функцией преобразования, вызываемой для пар элементов-приёмника и элементов-аргумента с одинаковыми позициями.
fun main() {
//sampleStart
val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
println(colors.zip(animals) { color, animal -> "The ${animal.replaceFirstChar { it.uppercase() }} is $color"})
//sampleEnd
}
Когда у вас есть List пар Pair, вы можете выполнить обратное преобразование — распаривание — которое создает две списка из этих пар:
Первый список содержит первые элементы каждой
Pairв исходном списке.Второй список содержит вторые элементы.
Для распаривания списка пар вызовите unzip().
fun main() {
//sampleStart
val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
println(numberPairs.unzip())
//sampleEnd
}
Ассоциирование
Преобразования ассоциирования позволяют создавать карты из элементов коллекции и определенных значений, связанных с ними. В различных типах ассоциации элементы могут быть либо ключами, либо значениями в ассоциативной карте.
Основная функция ассоциации associateWith() создаёт Map, в которой элементы исходной коллекции являются ключами, а значения производятся из них с помощью заданной функции преобразования. Если два элемента равны, только последний остаётся в карте.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateWith { it.length })
//sampleEnd
}
Для создания карт, в которых элементы коллекции являются значениями, существует функция associateBy(). Она принимает функцию, которая возвращает ключ на основе значения элемента. Если ключи двух элементов совпадают, только последний остается в карте.
associateBy() также можно вызвать с функцией преобразования значений.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateBy { it.first().uppercaseChar() })
println(numbers.associateBy(keySelector = { it.first().uppercaseChar() }, valueTransform = { it.length }))
//sampleEnd
}
Ещё один способ создания карт, в которых и ключи, и значения каким-то образом производятся из элементов коллекции, — функция associate(). Она принимает лямбда-функцию, которая возвращает Pair: ключ и значение соответствующей пары ключ-значение карты.
Обратите внимание, что associate() создаёт кратковременные Pair объекты, что может повлиять на производительность. Поэтому associate() следует использовать, когда производительность некритична или это предпочтительнее других вариантов.
Пример последнего случая — когда ключ и соответствующее значение генерируются из элемента вместе.
fun main() {
data class FullName (val firstName: String, val lastName: String)
fun parseFullName(fullName: String): FullName {
val nameParts = fullName.split(" ")
if (nameParts.size == 2) {
return FullName(nameParts[0], nameParts[1])
} else throw Exception("Wrong name format")
}
//sampleStart
val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell")
println(names.associate { name -> parseFullName(name).let { it.lastName to it.firstName } })
//sampleEnd
}
Здесь мы сначала применяем функцию преобразования к элементу, а затем формируем пару из свойств результата этой функции.
Сглаживание
Если вы работаете с вложенными коллекциями, вам могут пригодиться функции стандартной библиотеки, которые обеспечивают плоский доступ к элементам вложенных коллекций.
Первая функция — flatten(). Её можно вызвать для коллекции коллекций, например, для List списков Set. Функция возвращает единый List всех элементов вложенных коллекций.
fun main() {
//sampleStart
val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2))
println(numberSets.flatten())
//sampleEnd
}
Другая функция — flatMap(), предоставляет гибкий способ обработки вложенных коллекций. Она принимает функцию, которая отображает элемент коллекции в другую коллекцию. В результате flatMap() возвращает единый список результатов этой функции для всех элементов. Таким образом, flatMap() работает как последующие вызовы map() (с коллекцией в качестве результата отображения) и flatten().
data class StringContainer(val values: List<String>)
fun main() {
//sampleStart
val containers = listOf(
StringContainer(listOf("one", "two", "three")),
StringContainer(listOf("four", "five", "six")),
StringContainer(listOf("seven", "eight"))
)
println(containers.flatMap { it.values })
//sampleEnd
}
Представление в виде строки
Если вам нужно получить содержимое коллекции в удобочитаемом формате, используйте функции, которые преобразуют коллекции в строки: joinToString() и joinTo().
joinToString() строит одну String из элементов коллекции на основе предоставленных аргументов. joinTo() делает то же самое, но добавляет результат к заданному объекту Appendable.
При вызове с аргументами по умолчанию, функции возвращают результат, аналогичный вызову toString() для коллекции: String представлений элементов в строковом виде, разделённых запятыми с пробелами.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println(numbers)
println(numbers.joinToString())
val listString = StringBuffer("The list of numbers: ")
numbers.joinTo(listString)
println(listString)
//sampleEnd
}
Чтобы создать пользовательское строковое представление, вы можете указать его параметры в аргументах функции separator, prefix, и postfix. Результирующая строка начнётся с prefix и закончится postfix. separator будет следовать за каждым элементом, кроме последнего.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))
//sampleEnd
}
Для больших коллекций, вы можете указать limit — количество элементов, которые будут включены в результат. Если размер коллекции превышает limit, все остальные элементы будут заменены единственным значением аргумента truncated.
fun main() {
//sampleStart
val numbers = (1..100).toList()
println(numbers.joinToString(limit = 10, truncated = "<...>"))
//sampleEnd
}
Наконец, чтобы настроить представление самих элементов, предоставьте функцию transform.
fun main() {
//sampleStart
val numbers = listOf("one", "two", "three", "four")
println(numbers.joinToString { "Element: ${it.uppercase()}"})
//sampleEnd
}
© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/collection-transformations.html