Loops & Ranges

Kotlin · Lesson 9 of 29 6 min read Updated

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.

Kotlin
fun main() {
    for (i in 1..5) {
        println("7 x $i = ${7 * i}")
    }
}
Output
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.

Kotlin
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())
}
Output
[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
RangeValuesUse it for
1..51, 2, 3, 4, 5counting with both ends included
1..<51, 2, 3, 4positions in a list of size 5 (0..<5)
5 downTo 15, 4, 3, 2, 1counting backwards
0..20 step 50, 5, 10, 15, 20skipping values
'a'..'e'a, b, c, d, echaracters

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.

Kotlin
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")
}
Output
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.

Kotlin
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)
}
Output
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.

Kotlin
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
            }
        }
    }
}
Output
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.size goes one step too far and crashes. Use 0..<list.size or, better, list.indices.
  • Counting down with ... 5..1 is an empty range, so the loop never runs. Use 5 downTo 1.
  • Endless while loops. Forgetting years++ or the balance update keeps the condition true forever.
  • Changing the loop variable. In for (i in 1..5), i is read-only. Use a while loop if you need to control the counter yourself.

Key points

  • for (x in something) loops over ranges, strings and collections.
  • a..b includes both ends; a..<b leaves out b; downTo counts backwards; step skips.
  • withIndex() and indices give you positions when you need them.
  • while checks before each round; do-while runs at least once.
  • continue skips a round, break stops a loop, and break@label stops an outer loop.
  • repeat(n) { } runs a block n times; since Kotlin 2.2, break/continue work 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
Kotlin
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)
}
Output
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?

2. How many times does the body of for (i in 5..1) run?

3. What is special about a do-while loop?

4. In nested loops, how do you leave the outer loop from the inner one?

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