How do I use destructuring with maps and pairs in Kotlin?

In Kotlin, destructuring lets you unpack values from an object into separate variables.

It is commonly used with:

  • Pair
  • Map.Entry
  • loops over maps
  • lambda parameters

Destructuring a Pair

A Pair<A, B> contains two values: first and second.

val pair = "Alice" to 25

val (name, age) = pair

println(name) // Alice
println(age)  // 25

This is equivalent to:

val name = pair.first
val age = pair.second

You can also create a Pair explicitly:

val pair = Pair("Kotlin", 2.1)

val (language, version) = pair

println(language) // Kotlin
println(version)  // 2.1

Destructuring map entries in a loop

When you iterate over a map, each item is a Map.Entry<K, V>. Kotlin lets you destructure it into key and value:

val ages = mapOf(
    "Alice" to 25,
    "Bob" to 30
)

for ((name, age) in ages) {
    println("$name is $age years old")
}

Here:

(name, age)

means:

name = entry.key
age = entry.value

Destructuring in lambdas

You can destructure map entries inside lambda parameters too:

val scores = mapOf(
    "Alice" to 90,
    "Bob" to 85,
    "Charlie" to 95
)

scores.forEach { (name, score) ->
    println("$name scored $score")
}

You can also use it with operations like map, filter, and sortedBy:

val passingStudents = scores.filter { (_, score) ->
    score >= 90
}

println(passingStudents) // {Alice=90, Charlie=95}

Ignoring values with _

If you only need one part of the destructured value, use _ for the part you want to ignore.

val user = "Alice" to 25

val (name, _) = user

println(name) // Alice

With a map:

val ages = mapOf(
    "Alice" to 25,
    "Bob" to 30
)

for ((name, _) in ages) {
    println(name)
}

Or if you only need values:

for ((_, age) in ages) {
    println(age)
}

Destructuring mutable maps

Destructuring works the same way with mutable maps:

val inventory = mutableMapOf(
    "Apples" to 10,
    "Oranges" to 5
)

for ((item, count) in inventory) {
    println("$item: $count")
}

If you want to update values, use the key:

for ((item, count) in inventory) {
    inventory[item] = count + 1
}

println(inventory) // {Apples=11, Oranges=6}

Important detail: destructuring depends on componentN() functions

Destructuring works when a type provides functions like:

component1()
component2()

Pair has:

component1() // first
component2() // second

Map entries support destructuring as:

component1() // key
component2() // value

So this:

val (key, value) = "id" to 123

is essentially:

val pair = "id" to 123
val key = pair.component1()
val value = pair.component2()

Quick summary

val pair = "Alice" to 25
val (name, age) = pair

val map = mapOf(
    "Alice" to 25,
    "Bob" to 30
)

for ((key, value) in map) {
    println("$key -> $value")
}

map.forEach { (key, value) ->
    println("$key -> $value")
}

Use destructuring when it makes key-value or pair-based code easier to read.

How do I use destructuring declarations in Kotlin?

Destructuring declarations in Kotlin simplify the process of unpacking values from objects, data classes, maps, arrays, or collections into individual variables. This is particularly useful when working with complex objects or collections. Below is an explanation of how to use destructuring declarations in different scenarios.


1. Data Classes

Destructuring declarations work seamlessly with data classes. By default, each data class generates componentN() functions for its properties, allowing destructuring.

data class Person(val name: String, val age: Int)

fun main() {
    val person = Person("Alice", 25)

    // Destructure into two variables
    val (name, age) = person

    println("Name: $name, Age: $age")  // Output: Name: Alice, Age: 25
}

2. Pairs and Triples

Kotlin’s Pair and Triple classes also support destructuring.

fun main() {
    val pair = Pair("Kotlin", 2023)
    val (language, year) = pair

    println("Language: $language, Year: $year")  // Output: Language: Kotlin, Year: 2023

    val triple = Triple("A", "B", "C")
    val (first, second, third) = triple

    println("$first, $second, $third")  // Output: A, B, C
}

3. Maps

For maps, destructuring can be used in for loops.

fun main() {
    val map = mapOf("A" to 1, "B" to 2)

    for ((key, value) in map) {
        println("$key -> $value")
    }
}

4. Destructuring in Functions

You can destructure function parameters if they accept a Pair, Triple, or a destructurable object.

fun printPair(pair: Pair<String, Int>) {
    val (key, value) = pair
    println("$key -> $value")
}

fun main() {
    val pair = "Kotlin" to 2023
    printPair(pair)  // Output: Kotlin -> 2023
}

Or alternatively:

fun printPairDestructured(key: String, value: Int) {
    println("$key -> $value")
}

fun main() {
    val pair = "Kotlin" to 2023
    printPairDestructured(pair.first, pair.second)
    // OR use `destructuring` combined with named parameters
    val (key, value) = pair
    println("Declarative alternative using external scoped vars $key $value")
}

5. Arrays and Lists

You can destructure arrays and lists up to the number of elements you want to extract.

fun main() {
    val numbers = listOf(1, 2, 3)

    val (first, second, third) = numbers
    println("$first, $second, $third")  // Output: 1, 2, 3

    val array = arrayOf(10, 20, 30)
    val (a, b, c) = array
    println("$a, $b, $c")  // Output: 10, 20, 30
}

6. Custom Destructuring

If you want a custom class to support destructuring, you need to implement componentN() functions for the properties you want to destructure.

class Coordinates(val x: Int, val y: Int) {
    operator fun component1() = x
    operator fun component2() = y
}

fun main() {
    val point = Coordinates(10, 20)
    val (x, y) = point

    println("x: $x, y: $y")  // Output: x: 10, y: 20
}

7. Underscore (_) for Ignoring Values

If you don’t need all the components, you can ignore specific values using _.

fun main() {
    val person = Person("Alice", 25)
    val (name, _) = person

    println("Name: $name")  // Output: Name: Alice
}

8. With Loops

Destructuring is helpful in loops to extract values efficiently.

fun main() {
    val list = listOf(Pair("A", 1), Pair("B", 2))

    for ((char, number) in list) {
        println("$char -> $number")
    }
}

Notes:

  • Destructuring works because Kotlin generates componentN() functions automatically for data class properties.
  • The number of variables in the destructuring declaration must match the number of componentN() functions available for the object.

By using destructuring, you can write code that is more concise and easier to read.