Bitfield Consulting

View Original

Finding whether a Go map key exists

And getting free ice cream

How do you check if a map contains a key in Go? What happens if you look up a key in a Go map that doesn’t exist? How do you represent a set of objects as a map in Golang, and efficiently check whether a given value is in the set? You’ll be alternately astonished and delighted by what you learn in this friendly, helpful tutorial on Go maps (no money back if not astonished or delighted).

We previously learned about how to look up values in a Go map. So far, we've only looked up things that we already stored there. For example, here's our diner menu, mapping the names of dishes to prices:

menu := map[string]float64{
    "eggs": 1.75,
    "bacon": 3.22,
    "sausage": 1.89,
}

Looking up non-existent keys

You might be wondering what happens if we look up a key that isn't in the map. The result may surprise you:

fmt.Println(menu["ice cream"])
// -> 0

Free ice cream? Well, the manager may have something to say about that. In fact, a Go map lookup always returns the zero value for any missing keys. In this case, our value type is float64, so we'll get the zero value for float64, which is 0. Whatever the type of your map values, you'll get the zero value for that type.

So we can't actually tell just by looking it up whether or not a given key exists in a Go map. Maybe it existed, and the value was actually zero, or maybe it didn't exist, and we just got the default value.

Checking whether a key exists in the map

Sometimes it's useful to be able to tell whether or not a Go map key exists, and there's a special syntax for that:

price, ok := menu["ice cream"]
if ok {
    return fmt.Sprintf("Yes, ice cream is on the menu; it costs %.2f", price)
}
return fmt.Sprint("NO ICE CREAM FOR YOU")

The ok value is true if the key was found, and false otherwise. Good to know!

If we don't even care about the actual value, and we just want to know whether the key was in the map at all, we can ignore the first value altogether using the blank identifier (_):

_, ok := menu["grits"]

Boolean maps

It's common in Go to use maps as a kind of set, in the mathematical sense of a group of objects that something is either a member of, or it's not. We can use a map with a value type of bool for this.

For example, suppose we want to keep track of what's in the diner's stockroom. When supplies of, say, bacon, run out, we need to re-order it (very important).

Suppose we have a map called inStock which represents our current stock:

var inStock = map[string]bool{
    "eggs":    true,
    "bacon":   true,
    "sausage": true,
}

How can we now determine whether a given item, say, bacon, is in stock? We could use the value, ok syntax to set a variable based on whether the key existed, and then check the variable:

if _, ok := inStock[dish]; ok {
    return fmt.Sprintf("Yes, %s is in stock", dish)
}
return fmt.Sprintf("Sorry, we're all out of %s", dish)

This is a little lame, though. Since we know that maps return the zero value for mising keys, and we also happen to know that the zero value of bool is false, we can write simply:

if inStock[dish] {
    return fmt.Sprintf("Yes, %s is in stock", dish)
}
return fmt.Sprintf("Sorry, we're all out of %s", dish)

Much clearer! Because we chose the name of our map with care, this even reads a bit like English:

if in Stock ...

It would have been an easy mistake to call this variable stock, for example, but then the code wouldn't read half so well. The names of bool variables (including maps of bool) should be adjectives. Happy dining!

Yes, bacon is in stock
Sorry, we're all out of truffles

Next

This is part 4 of a Golang tutorial series on maps. Check out the other posts in the series:

We've learned a lot about how to manipulate individual map entries. But what about when we want to treat a map as a whole? For example, to loop over it, doing the same operation for each key-value pair? That's the topic of the next tutorial in this series, Iterating over maps.

Looking for help with Go?

If you found this article useful, and you'd like to learn more about Go, then I can help! I offer one-to-one or group training in Go development, for complete beginners through to experienced Gophers. Check out my Learn Golang with mentoring page to find out more, or email go@bitfieldconsulting.com. Looking forward to hearing from you!

Gopher image by egonelbre

See this content in the original post