Go has no set type
A slice keeps every value you put in it, repeats included. Sometimes you only care which values there are. A set holds each value once, and adding a value that is already there changes nothing.
Go has no built-in set, but a map gives you the same thing. A key can be in a map only once, so a map[string]bool whose keys are the values and whose values are true behaves like a set of strings.
pets := map[string]bool{} // an empty set
pets["cat"] = true
pets["dog"] = true
pets["cat"] = true // already in it, no change
fmt.Println(len(pets)) // 2
fmt.Println(pets["dog"]) // true
fmt.Println(pets["fish"]) // false
These lines print 2, true and false.
map[string]bool{}makes an empty set of strings.s[x] = trueaddsx.s[x]istruewhenxis in the set.len(s)is the number of values in the set.
Missing keys read as false
Reading a key that isn't in a map gives the zero value of the value type, which is false for bool. So if s[x] is a complete membership test, and it never fails on a missing key. The comma-ok form (_, ok := s[x]) works too; for pets["fish"] it gives ok as false. For a set where every stored value is true, the plain s[x] is enough.
The check is fast even for a large set, because a map finds a key directly. Looking for a value in a slice means comparing it with the elements one by one.
Building a set from a slice
Go can't convert a slice to a map in one expression. A for loop adds the values one at a time. A repeated value sets the same key again, so it is stored once.
A map has no order, so if you print its keys they may come out in any order.
The lesson's task
The slice names has some names twice. Build a set kids from it, add Zoe, add Ava again, then print Leo is coming if Leo is in the set.
- Create the empty set:
kids := map[string]bool{}. - Loop over
nameswithfor _, name := range namesand setkids[name] = true. The index goes into_. - After the first print, set
kids["Zoe"]andkids["Ava"]totrue. Ava is already a key, so only Zoe makes the set bigger. - Finish with
if kids["Leo"]and print the message.
func mainFunction() {
names := []string{"Ava", "Leo", "Ava", "Mia", "Leo"}
kids := map[string]bool{}
for _, name := range names {
kids[name] = true
}
fmt.Println(len(kids), "kids")
kids["Zoe"] = true
kids["Ava"] = true
fmt.Println(len(kids), "kids")
if kids["Leo"] {
fmt.Println("Leo is coming")
}
}
The output is:
3 kids
4 kids
Leo is coming
Five names go in, but Ava and Leo each appear twice, so the set holds three: Ava, Leo and Mia. Zoe makes four.
The same idea works for any comparable key type. A map[int]bool is a set of integers and a map[rune]bool is a set of characters. Only the key type changes; the way you add, test and count stays the same.
Common mistakes
- Using
len(names)to count kids. The slice still has the repeats; the set does not. - Storing
falsefor absent values. If you sets[x] = false, the key is still in the map and still counted bylen. Remove a value withdelete, not by setting it tofalse. - Expecting a fixed print order from the keys of a set.
Key takeaways
- A
map[T]boolis Go's usual set. - Add with
s[x] = true, test withs[x], count withlen(s). - A missing key reads as
false, so the test needs no extra check.
