Операции преобразования коллекций
Стандартная библиотека 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. Элементы коллекции-получателя являются первыми элементами этих пар.
Если коллекции имеют разный размер, размер результата 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, вы можете выполнить обратное преобразование — распаковку — которое создаёт две списка из этих пар:
Первый список содержит первые элементы каждой пары в исходном списке.
Второй список содержит вторые элементы.
Чтобы распаковать список пар, вызовите unzip().
fun main() {
//sampleStart
val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
println(numberPairs.unzip())
//sampleEnd
}
Связывание
Преобразования связывания позволяют создавать карты из элементов коллекции и определённых связанных с ними значений. В разных типах связывания элементы могут быть либо ключами, либо значениями в ассоциативной карте.
Основная функция связывания associateWith() создаёт карту, в которой элементы исходной коллекции являются ключами, а значения генерируются из них с помощью заданной функции преобразования. Если два элемента равны, остаётся только последний в карте.
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(). Она принимает лямбда-функцию, возвращающую пару «ключ-значение» соответствующей записи карты.
Обратите внимание, что 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ов. Функция возвращает единый список всех элементов вложенных коллекций.
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() строит одну строку из элементов коллекции, основываясь на предоставленных аргументах. joinTo() делает то же самое, но добавляет результат к заданному объекту Appendable.
Когда вызываются с аргументами по умолчанию, функции возвращают результат, аналогичный вызову toString() для коллекции: строку с представлениями элементов, разделёнными запятыми и пробелами.
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 — количество элементов, которое будет включено в результат. Если размер коллекции превышает предел, все остальные элементы будут заменены единственным значением аргумента 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–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/collection-transformations.html