Операции преобразования коллекций
Стандартная библиотека 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
}
Слияние
Преобразование слияния (zip) создаёт пары из элементов с одинаковыми позициями в обеих коллекциях. В стандартной библиотеке 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, вы можете выполнить обратное преобразование — разделение (unzip) — которое создаёт две списка из этих пар:
Первый список содержит первые элементы каждой
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() для коллекции: строку, содержащую строковые представления элементов, разделённые запятыми и пробелами.
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