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.
- Call
found.remove("qqq")andfound.remove("zzz"). The first call returnstrueand the secondfalse. - Below
var score = 0, loop withfor (word in found). - Inside, add
word.lengthtoscore.
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
removecalls after the firstprintln, so the printed count is 5 instead of 4. - Using
val scoreinstead ofvar 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.
