Start learning free

LESSON 238 OF 677 · SETS

Removing From a Set in Kotlin and Looping Over It

Lesson Read the full explanation

TL ; DR:

remove(x) takes x out of a set. A for loop visits every value once, in no fixed order.


val colors = hashSetOf("red", "blue", "green")
colors.remove("blue")
colors.remove("pink")           // not in it, nothing happens

for (color in colors) {
    println(color)              // red and green, in any order
}


Full lesson:

s.remove(x) removes x from the set s. It returns true if x was there. If it wasn't, nothing happens and it returns false.

for (x in s) visits each value of the set once. The order of a HashSet isn't fixed. It may differ from the order you added the values in.

So loop over a set when the order doesn't matter. A total, a count and a check are fine.

Don't remove values from a set while you loop over it. Kotlin stops with a ConcurrentModificationException. Remove first, then loop.


Your task

In a word game, the set found holds the words a player found. The words "qqq" and "zzz" aren't allowed. Remove both from found.

The player never found "zzz", so removing it changes nothing.

Then add up the points. A word scores one point per letter. Loop over found and add each word's length to score.

The code already prints how many words are left, and the points.

Expected output:

4 words
14 points
STEP 1 OF 3
    Ctrl + Enter to check

    KOTLIN · SETS

    Removing From Sets in Kotlin, explained

    remove and for

    s.remove(x) takes x out of the set s. It returns true if x was there and false if it was not. In the second case nothing happens: removing a value that is missing is not an error.

    val colors = hashSetOf("red", "blue", "green")
    colors.remove("blue")
    colors.remove("pink")           // not in it, nothing happens
    
    for (color in colors) {
        println(color)              // red and green, in any order
    }
    

    A for (x in s) loop visits each value of the set once. A HashSet does not keep a fixed order, so the values may come out in a different order than you added them. Loop over a set when the order does not matter: totals, counts and checks are all fine.

    Other ways to remove

    The operator form found -= "cat" does the same as found.remove("cat") on a mutable set. The check "cat" in found is the same as found.contains("cat"), and found.clear() empties the whole set, so its size becomes 0.

    Removing from a set affects only that set. The list the set was built from keeps all its values, repeats included.

    Do not remove while looping

    Removing values from a set while you loop over it makes Kotlin stop with a ConcurrentModificationException. Removing the three-letter words from a set inside for (w in found) throws it, for example. Remove first and loop afterwards. To remove many values by a rule, use removeAll with a condition:

    val f2 = words.toHashSet()
    f2.removeAll { it.length == 3 }
    println(f2.size)   // 2
    

    Here words is the lesson's list cat, tree, sun, qqq, moon. Three of its five words have three letters, so two are left.

    The lesson's task

    In a word game, the set found holds the words a player found. The words "qqq" and "zzz" are not allowed, so both must be removed. The player never found "zzz", so that call changes nothing. Then each remaining word scores one point per letter, and the loop adds up the points.

    1. Call found.remove("qqq") and found.remove("zzz"). The first call returns true and the second false.
    2. Below var score = 0, loop with for (word in found).
    3. Inside, add word.length to score.
    class Solution {
        fun mainFunction() {
            val words = mutableListOf("cat", "tree", "sun", "qqq", "moon")
            val found = words.toHashSet()
    
            found.remove("qqq")
            found.remove("zzz")
            println("${found.size} words")
    
            var score = 0
            for (word in found) {
                score += word.length
            }
            println("$score points")
        }
    }
    

    The output is:

    4 words
    14 points
    

    The four words left are cat, tree, sun and moon, with 3 + 4 + 3 + 4 = 14 letters. The sum is the same in any order, which is why a loop over a set is safe here.

    Using the answer of remove

    Because remove tells you whether the value was there, you can use it as a condition. With val s = hashSetOf("a", "b"), if (s.remove("a")) println("a was there") prints a was there, and the same line a second time prints nothing, since "a" is gone. This task does not need it, but it is handy.

    Once you know loops over collections, a library call can shorten the scoring: found.sumOf { it.length } also gives 14 for the four words. The loop is the version to learn first, because it shows exactly what happens.

    Common mistakes

    • Expecting an error when removing a value that is not in the set. There is none.
    • Putting the remove calls after the first println, so the printed count is 5 instead of 4.
    • Using val score instead of var score. The score changes in the loop.
    • Writing code that depends on the order of the loop.

    Key takeaways

    • remove(x) returns whether the value was there, and never fails on a missing one.
    • for (x in set) visits every value once, in no fixed order.
    • Do not remove from a set inside a loop over that set.
    Keep going with Kotlin500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free