Loops & Ranges
Computers are good at doing the same thing many times without getting bored: printing a multiplication table, adding up every order of the day, checking each seat in a train coach. Loops let you write the work once and repeat it. Ranges describe sequences of numbers or characters, and they pair naturally with loops.
In this lesson you will learn Kotlin's for, while and do-while loops, how to stop or skip with break and continue, and how to build ranges such as 1..10, 1..<10 and 10 downTo 1.
The for loop
A for loop goes through each item of something you can iterate (step through): a range, a string, a list. The loop variable takes each value in turn.
fun main() {
for (i in 1..5) {
println("7 x $i = ${7 * i}")
}
}7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35
1..5 is a range from 1 to 5, including both ends. The variable i is created by the loop itself; you do not write val or var for it, and it exists only inside the loop.
Ranges
A range is a value in its own right. You can print it as a list, test membership with in, and add it up.
fun main() {
println((1..5).toList())
println((1..<5).toList())
println((10 downTo 0 step 3).toList())
println((0..20 step 5).toList())
println(('a'..'e').toList())
println(7 in 1..10)
println(15 !in 1..10)
println((1..100).sum())
}[1, 2, 3, 4, 5] [1, 2, 3, 4] [10, 7, 4, 1] [0, 5, 10, 15, 20] [a, b, c, d, e] true true 5050
| Range | Values | Use it for |
|---|---|---|
1..5 | 1, 2, 3, 4, 5 | counting with both ends included |
1..<5 | 1, 2, 3, 4 | positions in a list of size 5 (0..<5) |
5 downTo 1 | 5, 4, 3, 2, 1 | counting backwards |
0..20 step 5 | 0, 5, 10, 15, 20 | skipping values |
'a'..'e' | a, b, c, d, e | characters |
Looping over strings and collections
for works directly on strings (one character at a time) and on lists. When you also need the position, use withIndex(), which gives an index and a value together, or loop over indices.
fun main() {
val batters = listOf("Aarav", "Kabir", "Rohan")
for (name in batters) {
println("Batting: $name")
}
for ((index, name) in batters.withIndex()) {
println("${index + 1}. $name")
}
var vowels = 0
for (ch in "Ahmedabad") {
if (ch.lowercaseChar() in "aeiou") {
vowels++
}
}
println("Vowels: $vowels")
}Batting: Aarav Batting: Kabir Batting: Rohan 1. Aarav 2. Kabir 3. Rohan Vowels: 4
(index, name) unpacks two values at once; this is called destructuring, and you will use it a lot with maps and data classes. ch in "aeiou" checks whether the character appears in that string.
while and do-while
A while loop repeats as long as its condition is true. Use it when you do not know in advance how many times to repeat. A do-while loop checks the condition after the body, so the body always runs at least once.
fun main() {
var balance = 10_000
var years = 0
while (balance < 15_000) {
balance += balance * 8 / 100
years++
}
println("After $years years: Rs $balance")
var attempt = 0
do {
attempt++
println("Connecting, attempt $attempt")
} while (attempt < 3)
}After 6 years: Rs 15867 Connecting, attempt 1 Connecting, attempt 2 Connecting, attempt 3
The first loop grows a deposit by 8 percent each year (using whole rupees) until it reaches Rs 15,000. Make sure something inside a while loop changes the condition; otherwise the loop never ends.
break, continue and labels
continue skips the rest of the current round and moves to the next one. break leaves the loop completely. In nested loops, break only leaves the innermost loop. To leave an outer loop, give it a label (a name followed by @) and write break@label.
fun main() {
val orders = listOf(250, -1, 480, 0, 1200, 90)
var total = 0
for (amount in orders) {
if (amount <= 0) continue
if (amount > 1000) {
println("Order of Rs $amount needs approval, stopping")
break
}
total += amount
}
println("Total processed: Rs $total")
outer@ for (row in 1..3) {
for (seat in 1..3) {
if (row == 2 && seat == 2) {
println("Free seat at row $row, seat $seat")
break@outer
}
}
}
}Order of Rs 1200 needs approval, stopping Total processed: Rs 730 Free seat at row 2, seat 2
The invalid amounts -1 and 0 are skipped, 250 and 480 are added, and the loop stops at 1200, so 90 is never reached.
repeat, and break inside lambdas
When you just need to do something n times, repeat(n) { } is shorter than a for loop. The block receives the round number, starting at 0.
Common mistakes
- Off-by-one errors.
0..list.sizegoes one step too far and crashes. Use0..<list.sizeor, better,list.indices. - Counting down with
...5..1is an empty range, so the loop never runs. Use5 downTo 1. - Endless
whileloops. Forgettingyears++or the balance update keeps the conditiontrueforever. - Changing the loop variable. In
for (i in 1..5),iis read-only. Use awhileloop if you need to control the counter yourself.
Key points
for (x in something)loops over ranges, strings and collections.a..bincludes both ends;a..<bleaves outb;downTocounts backwards;stepskips.withIndex()andindicesgive you positions when you need them.whilechecks before each round;do-whileruns at least once.continueskips a round,breakstops a loop, andbreak@labelstops an outer loop.repeat(n) { }runs a blockntimes; since Kotlin 2.2,break/continuework inside inline lambdas in loops.
Exercise
Print the numbers from 1 to 30 for a classroom game, but print Fizz for multiples of 3, Buzz for multiples of 5 and FizzBuzz for multiples of both, all on one line separated by spaces. Stop the loop completely if you reach 22. Use a for loop, when and break, and build the line with buildString.
Show solution
fun main() {
val line = buildString {
for (n in 1..30) {
if (n == 22) break
val word = when {
n % 15 == 0 -> "FizzBuzz"
n % 3 == 0 -> "Fizz"
n % 5 == 0 -> "Buzz"
else -> n.toString()
}
if (n > 1) append(' ')
append(word)
}
}
println(line)
}1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz
The check for 15 comes first, because a multiple of 15 is also a multiple of 3 and 5. Adding the space before every word except the first avoids a stray space at the end.
Quick Quiz
1. Which range contains 1, 2, 3 and 4 but not 5?
..< leaves out the end value.
2. How many times does the body of for (i in 5..1) run?
5..1 is an empty range; use 5 downTo 1 to count backwards.
3. What is special about a do-while loop?
The condition is checked after the body, so the first round always happens.
4. In nested loops, how do you leave the outer loop from the inner one?
A plain break leaves only the innermost loop.
Learn Kotlin with a mentor
Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
