delete takes a key out
In Go a set is usually a map with bool values. The built-in delete(s, x) removes the key x from the map. If x isn't there, nothing happens and there is no error.
colors := map[string]bool{"red": true, "blue": true, "green": true}
delete(colors, "blue")
delete(colors, "pink") // not in it, nothing happens
for color := range colors {
fmt.Println(color) // red and green, in any order
}
Setting colors["blue"] = false would not do the same job: the key would stay in the map and still count in len. Use delete to remove a value from a set.
Looping over a set
for x := range s visits each key once. With a single variable, range over a map gives only the keys, which is all a set has to offer.
The visiting order is not fixed, and Go deliberately varies it from one run to the next. Loop over a set when the order doesn't matter: a total, a count or a check gives the same answer in any order.
Deleting while you loop is a related question. Go allows delete inside a range loop over the same map, and a key removed before the loop reaches it is not visited. In this lesson the two steps are kept apart anyway: first the unwanted words are removed, then the loop adds up what is left. That order also makes the final count easy to check by eye.
It also helps to know how little delete returns. It gives back nothing, so there is no value to test to learn whether the key was present. If you need to know, look first with the comma-ok form (_, ok := found[word]) and then delete.
The lesson's task
The set found holds the words a player found. The words "qqq" and "zzz" aren't allowed, so both must be removed. The player never found "zzz", so removing it changes nothing. Then each remaining word scores one point per letter, and the loop adds up the points.
- Call
delete(found, "qqq")anddelete(found, "zzz"). - Below
score := 0, writefor word := range found, keeping only the key. - Inside, add
len(word)toscore.
func mainFunction() {
words := []string{"cat", "tree", "sun", "qqq", "moon"}
found := map[string]bool{}
for _, word := range words {
found[word] = true
}
delete(found, "qqq")
delete(found, "zzz")
fmt.Println(len(found), "words")
score := 0
for word := range found {
score += len(word)
}
fmt.Println(score, "points")
}
The output is:
4 words
14 points
Five words went in and "qqq" came out, leaving four. The remaining words have 3, 4, 3 and 4 letters, which add up to 14 whatever order the loop visits them in.
Common mistakes
- Setting the key to
falseinstead of deleting it.lenwould still count it, and ranging over the map would still visit it. - Worrying about deleting a missing key. It is safe; no check is needed first.
- Relying on the loop order. Sums and counts are safe. Printing the words one by one can differ between runs.
A last point on len: it counts keys, so after the two delete calls it reports 4, not 5 and not 3. The call for "zzz" removed nothing, which is why the count only dropped by one.
Key takeaways
delete(s, x)removes a key and does nothing when it is absent.for x := range sgives each element of the set once, in no fixed order.
