Start learning free

LESSON 234 OF 671 · SETS

Sets in Go: Using map[string]bool

Lesson Read the full explanation

TL ; DR:

Go has no set type. A map with bool values does the job. Each value is a key, set to true.


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


Full lesson:

A slice keeps every value you put in it, repeats too. Sometimes you only need to know which values there are. A set is made for that.

A set holds each value once. Adding a value that is already in it changes nothing.

Go has no set type. A map with bool values does the job. A key is in a map only once, just as a set needs.

  • map[string]bool{} makes an empty set of strings.
  • s[x] = true puts x in the set s.
  • s[x] is true when x is in the set.
  • len(s) gives how many values the set holds.

Reading a key that isn't there gives false, the zero value of bool. So if s[x] is enough. The comma-ok idiom from Maps & the ok Idiom works too.

Checking s[x] is fast, even for a big set. In a slice, you'd look at the values one by one.

Go can't build the map straight from a slice. A for loop puts the values in, one at a time. A repeat sets the same key again, so it is stored once.

A map has no order. Its keys may come out in any order.


Your task

The camp's sign-up slice, names, has some names twice. Build a set named kids from it: an empty map[string]bool, filled by a loop.

Then Zoe signs up, and Ava signs up again. Add both names to kids.

Last, if "Leo" is in kids, print Leo is coming.

The code already prints the number of kids twice.

Expected output:

3 kids
4 kids
Leo is coming
STEP 1 OF 5
    Ctrl + Enter to check

    GO · SETS

    Sets in Go, explained

    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] = true adds x.
    • s[x] is true when x is 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.

    1. Create the empty set: kids := map[string]bool{}.
    2. Loop over names with for _, name := range names and set kids[name] = true. The index goes into _.
    3. After the first print, set kids["Zoe"] and kids["Ava"] to true. Ava is already a key, so only Zoe makes the set bigger.
    4. 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 false for absent values. If you set s[x] = false, the key is still in the map and still counted by len. Remove a value with delete, not by setting it to false.
    • Expecting a fixed print order from the keys of a set.

    Key takeaways

    • A map[T]bool is Go's usual set.
    • Add with s[x] = true, test with s[x], count with len(s).
    • A missing key reads as false, so the test needs no extra check.
    Keep going with Go500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free