What a Pair is
Some values belong together: a pet's name and age, a point's x and y. Kotlin keeps two such values in a Pair. You make one with the constructor or with the to function, and both give the same pair:
val pet = Pair("Rex", 4)
val same = "Rex" to 4
println(pet == same)
true
The type of pet is Pair<String, Int>: the first slot holds a String, the second an Int. The two slots can have different types.
Reading and unpacking
Read the slots with .first and .second. Or unpack the pair into two new variables in one line, with parentheses on the left of the =:
val pet = Pair("Rex", 4)
println(pet.first)
println(pet.second)
val (name, age) = pet
println("$name is $age")
Rex
4
Rex is 4
The names are matched to the slots by position: the first name gets first, the second gets second. The names themselves are up to you, so pick ones that say what the values mean.
Returning two values from a function
A function returns one value, but that value can be a pair. The return type is the pair's type, here Pair<Int, Int>. The caller unpacks the result straight away:
fun splitTime(minutes: Int): Pair<Int, Int> {
return Pair(minutes / 60, minutes % 60)
}
val (hours, mins) = splitTime(135)
println("$hours h $mins min")
2 h 15 min
Integer division drops the remainder, so 135 / 60 is 2 whole hours, and 135 % 60 is the 15 minutes left.
A pair can't be changed
first and second are vals, so pet.second = 5 doesn't compile. To get a different pair, make a new one with copy:
val older = pet.copy(second = 5)
println(older)
println(pet)
(Rex, 5)
(Rex, 4)
Pair is a data class, which is why printing it shows its values in parentheses, and why two pairs with equal values are equal with ==. The original pair stays as it was.
Pairs in a list
Pairs are often collected in a list, and a for loop can unpack each one as it goes, the same way:
val pets = listOf("Rex" to 4, "Bella" to 7)
for ((name, age) in pets) {
println("$name is $age")
}
Rex is 4
Bella is 7
If you only need one of the two values, write an underscore for the other: val (_, age) = pets[1] gives age the value 7 and creates no variable for the name.
For three values Kotlin has Triple, which works the same way with first, second and third: val (p, q, r) = Triple(1, "a", true) gives p = 1, q = "a" and r = true. For more than three values, a class of your own with named fields is clearer.
The exercise
The lesson's class has two tasks in mainFunction and one in splitTime:
- Make
splitTimereturn the whole hours and the minutes left. - Unpack
petintonameandage. - Call
splitTime(135)and unpack the result intohoursandmins.
The finished code:
class Solution {
fun splitTime(minutes: Int): Pair<Int, Int> {
return Pair(minutes / 60, minutes % 60)
}
fun mainFunction() {
val pet = Pair("Rex", 4)
println(pet.first)
val (name, age) = pet
println("$name is $age")
val (hours, mins) = splitTime(135)
println("$name walked $hours h $mins min")
}
}
It prints the expected three lines:
Rex
Rex is 4
Rex walked 2 h 15 min
Notice that the last line uses name from step 2. Unpacking pet is what creates that variable, so the order of the steps matters.
Common mistakes
- Swapping the order when unpacking.
val (age, name) = petcompiles, because the types are not checked against your names, butagenow holds"Rex". - Forgetting the parentheses.
val name, age = petis not unpacking. - Trying to assign to
firstorsecond. Usecopyinstead.
Key takeaways
Pair(a, b)anda to bmake the same pair, of typePair<A, B>.- Read the slots with
.firstand.second, or unpack withval (x, y) = pair. - A function can return a pair to give back two values at once.
- A pair is read-only;
copymakes a changed version.
