Skip to content

컬렉션 변환 연산

Kotlin 표준 라이브러리는 컬렉션 _변환_을 위한 확장 함수 세트를 제공합니다. 이 함수들은 제공된 변환 규칙에 따라 기존 컬렉션에서 새로운 컬렉션을 구성합니다. 이 페이지에서는 사용 가능한 컬렉션 변환 함수에 대한 개요를 제공합니다.

매핑 변환은 다른 컬렉션의 요소에 대한 함수의 결과로부터 컬렉션을 생성합니다. 기본 매핑 함수는 map()입니다. 이 함수는 주어진 람다 함수를 각 후속 요소에 적용하고 람다 결과 목록을 반환합니다. 결과의 순서는 원래 요소의 순서와 동일합니다. 요소 인덱스를 인수로 추가 사용하는 변환을 적용하려면 mapIndexed()를 사용합니다.

kotlin

fun main() {
    val numbers = setOf(1, 2, 3)
    println(numbers.map { it * 3 })
    println(numbers.mapIndexed { idx, value -> value * idx })
}

변환이 특정 요소에 대해 null을 생성하는 경우, map() 대신 mapNotNull() 함수를 호출하거나, mapIndexed() 대신 mapIndexedNotNull()를 호출하여 결과 컬렉션에서 null을 필터링할 수 있습니다.

kotlin

fun main() {
    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 })
}

맵을 변환할 때는 두 가지 옵션이 있습니다: 값을 변경하지 않고 키를 변환하거나 그 반대로 할 수 있습니다. 주어진 변환을 키에 적용하려면 mapKeys()를 사용하고, 반대로 mapValues()는 값을 변환합니다. 두 함수 모두 맵 엔트리를 인수로 받는 변환을 사용하므로, 키와 값 모두를 조작할 수 있습니다.

kotlin

fun main() {
    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 })
}

Zip

Zipping 변환은 두 컬렉션에서 동일한 위치에 있는 요소들로 쌍을 구성하는 것입니다. Kotlin 표준 라이브러리에서는 zip() 확장 함수를 통해 이 작업이 수행됩니다.

다른 컬렉션(또는 배열)을 인수로 사용하여 컬렉션 또는 배열에서 호출될 때, zip()Pair 객체들의 List를 반환합니다. 수신 컬렉션의 요소들은 이 쌍들에서 첫 번째 요소가 됩니다.

컬렉션의 크기가 다를 경우, zip()의 결과는 더 작은 크기가 됩니다; 더 큰 컬렉션의 마지막 요소들은 결과에 포함되지 않습니다.

zip()은 또한 중위(infix) 형식인 a zip b로 호출될 수 있습니다.

kotlin

fun main() {
    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))
}

두 개의 매개변수(수신 요소와 인수 요소)를 받는 변환 함수와 함께 zip()을 호출할 수도 있습니다. 이 경우, 결과 List는 동일한 위치에 있는 수신 요소와 인수 요소의 쌍에 대해 호출된 변환 함수의 반환 값을 포함합니다.

kotlin

fun main() {
    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"})
}

Pair들의 List를 가지고 있을 때, 이 쌍들로부터 두 개의 리스트를 구성하는 역변환, 즉 _압축 해제(unzipping)_를 수행할 수 있습니다:

  • 첫 번째 리스트는 원래 리스트에 있는 각 Pair의 첫 번째 요소를 포함합니다.
  • 두 번째 리스트는 두 번째 요소들을 포함합니다.

쌍 리스트를 압축 해제하려면 unzip()를 호출합니다.

kotlin

fun main() {
    val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
    println(numberPairs.unzip())
}

Associate

연관(Association) 변환은 컬렉션 요소와 그와 연관된 특정 값들로부터 맵을 구성할 수 있게 합니다. 다양한 연관 타입에서, 요소들은 연관 맵의 키 또는 값이 될 수 있습니다.

기본 연관 함수인 associateWith()는 원래 컬렉션의 요소들이 키가 되고, 주어진 변환 함수에 의해 그 요소들로부터 값이 생성되는 Map을 생성합니다. 두 요소가 같으면 마지막 요소만 맵에 남습니다.

kotlin

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    println(numbers.associateWith { it.length })
}

컬렉션 요소를 값으로 사용하여 맵을 구성하려면 associateBy() 함수가 있습니다. 이 함수는 요소의 값을 기반으로 키를 반환하는 함수를 인수로 받습니다. 두 요소의 키가 같으면 마지막 요소만 맵에 남습니다.

associateBy()는 값 변환 함수와 함께 호출될 수도 있습니다.

kotlin

fun main() {
    val numbers = listOf("one", "two", "three", "four")

    println(numbers.associateBy { it.first().uppercaseChar() })
    println(numbers.associateBy(keySelector = { it.first().uppercaseChar() }, valueTransform = { it.length }))
}

키와 값 모두가 컬렉션 요소로부터 어떤 방식으로든 생성되는 맵을 구성하는 또 다른 방법은 associate() 함수입니다. 이 함수는 해당 맵 엔트리의 키와 값인 Pair를 반환하는 람다 함수를 인수로 받습니다.

associate()는 성능에 영향을 줄 수 있는 수명이 짧은 Pair 객체를 생성한다는 점에 유의하십시오. 따라서 associate()는 성능이 중요하지 않거나 다른 옵션보다 더 선호될 때 사용해야 합니다.

후자의 예는 키와 해당 값이 요소로부터 함께 생성될 때입니다.

kotlin

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")
}

    val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell")
    println(names.associate { name -> parseFullName(name).let { it.lastName to it.firstName } })  
}

여기서는 먼저 요소에 변환 함수를 호출하고, 그 함수의 결과 속성에서 쌍을 구성합니다.

Flatten

중첩된 컬렉션을 다룰 경우, 중첩된 컬렉션 요소에 대한 플랫 액세스를 제공하는 표준 라이브러리 함수가 유용하다는 것을 알 수 있습니다.

첫 번째 함수는 flatten()입니다. 예를 들어, Set들의 List와 같이 컬렉션들의 컬렉션에서 이 함수를 호출할 수 있습니다. 이 함수는 중첩된 컬렉션의 모든 요소를 포함하는 단일 List를 반환합니다.

kotlin

fun main() {
    val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2))
    println(numberSets.flatten())
}

또 다른 함수인 flatMap()는 중첩된 컬렉션을 처리하는 유연한 방법을 제공합니다. 이 함수는 컬렉션 요소를 다른 컬렉션으로 매핑하는 함수를 인수로 받습니다. 결과적으로 flatMap()은 모든 요소에 대한 반환 값들의 단일 리스트를 반환합니다. 따라서 flatMap()map() (매핑 결과로 컬렉션을 사용하는)과 flatten()을 연속적으로 호출하는 것처럼 동작합니다.

kotlin

data class StringContainer(val values: List<String>)

fun main() {
    val containers = listOf(
        StringContainer(listOf("one", "two", "three")),
        StringContainer(listOf("four", "five", "six")),
        StringContainer(listOf("seven", "eight"))
    )
    println(containers.flatMap { it.values })
}

문자열 표현

컬렉션 내용을 읽기 쉬운 형식으로 검색해야 하는 경우, 컬렉션을 문자열로 변환하는 함수인 joinToString()joinTo()를 사용합니다.

joinToString()은 제공된 인수를 기반으로 컬렉션 요소로부터 단일 String을 구성합니다. joinTo()는 동일한 작업을 수행하지만, 결과를 주어진 Appendable 객체에 추가합니다.

매개변수의 기본값으로 호출될 때, 이 함수들은 컬렉션에 toString()을 호출하는 것과 유사한 결과를 반환합니다: 요소들의 문자열 표현이 공백과 쉼표로 구분된 String입니다.

kotlin

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    
    println(numbers)         
    println(numbers.joinToString())
    
    val listString = StringBuffer("The list of numbers: ")
    numbers.joinTo(listString)
    println(listString)
}

사용자 지정 문자열 표현을 구성하려면, 함수 인수인 separator, prefix, postfix에 해당 매개변수를 지정할 수 있습니다. 결과 문자열은 prefix로 시작하여 postfix로 끝납니다. separator는 마지막 요소를 제외한 각 요소 뒤에 옵니다.

kotlin

fun main() {
    val numbers = listOf("one", "two", "three", "four")    
    println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))
}

더 큰 컬렉션의 경우, 결과에 포함될 요소의 수인 limit를 지정하고 싶을 수 있습니다. 컬렉션 크기가 limit를 초과하면, 다른 모든 요소는 truncated 인수의 단일 값으로 대체됩니다.

kotlin

fun main() {
    val numbers = (1..100).toList()
    println(numbers.joinToString(limit = 10, truncated = "<...>"))
}

마지막으로, 요소 자체의 표현을 사용자 지정하려면 transform 함수를 제공하십시오.

kotlin

fun main() {
    val numbers = listOf("one", "two", "three", "four")
    println(numbers.joinToString { "Element: ${it.uppercase()}"})
}