1323

I know I can iterate over a map m with

for k, v := range m { ... }

and look for a key, but is there a more efficient way of testing for a key's existence in a map?

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
grokus
  • 18,046
  • 9
  • 29
  • 35

11 Answers11

2530

Here's how you check if a map contains a key.

val, ok := myMap["foo"]
// If the key exists
if ok {
    // Do something
}

This initializes two variables. val is the value of "foo" from the map if it exists, or a "zero value" if it doesn't (in this case the empty string). ok is a bool that will be set to true if the key existed.

If you want, you can shorten this to a one-liner.

if val, ok := myMap["foo"]; ok {
    //do something here
}

Go allows you to put an initializing statement before the condition (notice the semicolon) in the if statement. The consequence of this is that the scope ofval and ok will be limited to the body of the if statement, which is helpful if you only need to access them there.

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
marketer
  • 41,507
  • 11
  • 37
  • 40
  • What would happen if one line above `if val,ok ...` a val was declared: `var val string = ""`? – Kiril May 07 '14 at 15:49
  • 22
    @Kiril `var val string = ""` will remain the same, `val, ok :=` creates a new local variable with the same name that's only visibile in that block. – OneOfOne May 20 '14 at 01:22
  • 1
    @Mheni, I know I'm a bit late here, but [this question](https://stackoverflow.com/questions/29677670/what-is-the-big-o-performance-of-maps-in-golang) discusses the lookup complexity in go. Most of the time the [amortized complexity](http://www.cs.cornell.edu/courses/cs3110/2011sp/Lectures/lec20-amortized/amortized.htm) is O(1) but it's worth reading the answers on that question. – 3ocene Oct 09 '18 at 20:50
  • Notice that if you have an 'AND' condition it must go after the key exists. Is there any way around this? – Prashant Saraswat May 08 '19 at 18:11
  • @PrashantSaraswat nope, if you want the AND to come before, you'll need to break up the if statement. – Brandon Jan 02 '21 at 21:30
  • how would you do the reverse? If the key was NOT in the map? – alex Nov 13 '21 at 05:35
  • 2
    @alex `if _, ok := dict["foo"]; !ok {}` – Nearoo Jan 01 '22 at 14:39
  • 1
    If you only want to check the existence of a certain key, you can use the blank identifier (_) in place of val. Like so: `if _, ok := dict["foo"]; ok { //do something here }` – Emeka Augustine Feb 16 '22 at 22:33
  • 1
    Is is possible to check if a key exists in two different maps within the same if statement? – Neniel Jun 07 '22 at 20:24
  • It's interesting that val will also be in the scope of the else clause (if present) – Alessandro Martin May 14 '23 at 16:09
  • 1
    Kinda confusing that `(x, err)` and `(x, ok)` are both used by Go. – BallpointBen Aug 14 '23 at 21:07
198

In addition to The Go Programming Language Specification, you should read Effective Go. In the section on maps, they say, amongst other things:

An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.

attended := map[string]bool{
    "Ann": true,
    "Joe": true,
    ...
}

if attended[person] { // will be false if person is not in the map
    fmt.Println(person, "was at the meeting")
}

Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that 0 because it's not in the map at all? You can discriminate with a form of multiple assignment.

var seconds int
var ok bool
seconds, ok = timeZone[tz]

For obvious reasons this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report:

func offset(tz string) int {
    if seconds, ok := timeZone[tz]; ok {
        return seconds
    }
    log.Println("unknown time zone:", tz)
    return 0
}

To test for presence in the map without worrying about the actual value, you can use the blank identifier (_) in place of the usual variable for the value.

_, present := timeZone[tz]
peterSO
  • 158,998
  • 31
  • 281
  • 276
  • 1
    https://go.dev/play/p/X4liolwTsVL didn't work – ar-siddiqui Jan 20 '22 at 23:12
  • @ar-siddiqui: peterSO's idea was that you initialized "person" before using it, but here you go, a full working example: https://go.dev/play/p/_51LQmwicuO – BMDan Oct 26 '22 at 18:05
  • This works for primitives but as soon as you have map[somePrimitive]someStruct{} it won't work, you would need to go further to have a boolean statement for go to work with, see example: https://go.dev/play/p/LVvS0ojOJpI – fullStackChris Feb 02 '23 at 13:38
114

Searched on the go-nuts email list and found a solution posted by Peter Froehlich on 11/15/2009.

package main

import "fmt"

func main() {
        dict := map[string]int {"foo" : 1, "bar" : 2}
        value, ok := dict["baz"]
        if ok {
                fmt.Println("value: ", value)
        } else {
                fmt.Println("key not found")
        }
}

Or, more compactly,

if value, ok := dict["baz"]; ok {
    fmt.Println("value: ", value)
} else {
    fmt.Println("key not found")
}

Note, using this form of the if statement, the value and ok variables are only visible inside the if conditions.

grokus
  • 18,046
  • 9
  • 29
  • 35
  • 30
    If you really are just interested in whether the key exists or not, and don't care about the value, you can use `_, ok := dict["baz"]; ok`. The `_` part throws the value away instead of creating a temporary variable. – Matthew Crumley Jan 12 '10 at 17:03
54

Short Answer

_, exists := timeZone[tz]    // Just checks for key existence
val, exists := timeZone[tz]  // Checks for key existence and retrieves the value

Example

Here's an example at the Go Playground.

Longer Answer

Per the Maps section of Effective Go:

An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0.

Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that the empty string because it's not in the map at all? You can discriminate with a form of multiple assignment.

var seconds int
var ok bool
seconds, ok = timeZone[tz]

For obvious reasons this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false. Here's a function that puts it together with a nice error report:

func offset(tz string) int {
    if seconds, ok := timeZone[tz]; ok {
        return seconds
    }
    log.Println("unknown time zone:", tz)
    return 0
}

To test for presence in the map without worrying about the actual value, you can use the blank identifier (_) in place of the usual variable for the value.

_, present := timeZone[tz]
Community
  • 1
  • 1
Matthew Rankin
  • 457,139
  • 39
  • 126
  • 163
40

Have a look at this snippet of code

nameMap := make(map[string]int)
nameMap["river"] = 33
v ,exist := nameMap["river"]
if exist {
    fmt.Println("exist ",v)
}
Mattia Righetti
  • 1,265
  • 1
  • 18
  • 31
riverfan
  • 475
  • 4
  • 7
27

As noted by other answers, the general solution is to use an index expression in an assignment of the special form:

v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
var v, ok T = a[x]

This is nice and clean. It has some restrictions though: it must be an assignment of special form. Right-hand side expression must be the map index expression only, and the left-hand expression list must contain exactly 2 operands, first to which the value type is assignable, and a second to which a bool value is assignable. The first value of the result of this special form will be the value associated with the key, and the second value will tell if there is actually an entry in the map with the given key (if the key exists in the map). The left-hand side expression list may also contain the blank identifier if one of the results is not needed.

It's important to know that if the indexed map value is nil or does not contain the key, the index expression evaluates to the zero value of the value type of the map. So for example:

m := map[int]string{}
s := m[1] // s will be the empty string ""
var m2 map[int]float64 // m2 is nil!
f := m2[2] // f will be 0.0

fmt.Printf("%q %f", s, f) // Prints: "" 0.000000

Try it on the Go Playground.

So if we know that we don't use the zero value in our map, we can take advantage of this.

For example if the value type is string, and we know we never store entries in the map where the value is the empty string (zero value for the string type), we can also test if the key is in the map by comparing the non-special form of the (result of the) index expression to the zero value:

m := map[int]string{
    0: "zero",
    1: "one",
}

fmt.Printf("Key 0 exists: %t\nKey 1 exists: %t\nKey 2 exists: %t",
    m[0] != "", m[1] != "", m[2] != "")

Output (try it on the Go Playground):

Key 0 exists: true
Key 1 exists: true
Key 2 exists: false

In practice there are many cases where we don't store the zero-value value in the map, so this can be used quite often. For example interfaces and function types have a zero value nil, which we often don't store in maps. So testing if a key is in the map can be achieved by comparing it to nil.

Using this "technique" has another advantage too: you can check existence of multiple keys in a compact way (you can't do that with the special "comma ok" form). More about this: Check if key exists in multiple maps in one condition

Getting the zero value of the value type when indexing with a non-existing key also allows us to use maps with bool values conveniently as sets. For example:

set := map[string]bool{
    "one": true,
    "two": true,
}

fmt.Println("Contains 'one':", set["one"])

if set["two"] {
    fmt.Println("'two' is in the set")
}
if !set["three"] {
    fmt.Println("'three' is not in the set")
}

It outputs (try it on the Go Playground):

Contains 'one': true
'two' is in the set
'three' is not in the set

See related: How can I create an array that contains unique strings?

icza
  • 389,944
  • 63
  • 907
  • 827
  • 2
    what is `T` in `var v, ok T = a[x]`? isn't `ok` must be bool? – Kokizzu May 12 '17 at 04:31
  • 3
    @Kokizzu That's the general form of the variable declaration. At first we might think it would only work (compile) if the map would be of type `map[bool]bool` and `T` is `bool`, but it also works if map is of type `map[interface{}]bool` and `T` is `interface{}`; moreover it also works with custom types having `bool` as underlying type, see all on the [**Go Playground**](https://play.golang.org/p/lqJa8BZv49). So since that form is valid with multiple types substituted for `T`, that's why the general `T` is used. Type of `ok` can be anything to which an **untyped `bool`** can be assigned. – icza May 12 '17 at 07:53
26
var d map[string]string
value, ok := d["key"]
if ok {
    fmt.Println("Key Present ", value)
} else {
    fmt.Println(" Key Not Present ")
}
Davit Tovmasyan
  • 3,238
  • 2
  • 20
  • 36
chandra
  • 333
  • 4
  • 5
6
    var empty struct{}
    var ok bool
    var m map[string]struct{}
    m = make(map[string]struct{})
    m["somestring"] = empty


    _, ok = m["somestring"]
    fmt.Println("somestring exists?", ok) 
    _, ok = m["not"]
    fmt.Println("not exists?", ok)

Then, go run maps.go somestring exists? true not exists? false

Lady_Exotel
  • 71
  • 1
  • 1
5

It is mentioned under "Index expressions".

An index expression on a map a of type map[K]V used in an assignment or initialization of the special form

v, ok = a[x] 
v, ok := a[x] 
var v, ok = a[x]

yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.

mroman
  • 1,354
  • 9
  • 14
4

A two value assignment can be used for this purpose. Please check my sample program below

package main

import (
    "fmt"
)

func main() {
    //creating a map with 3 key-value pairs
    sampleMap := map[string]int{"key1": 100, "key2": 500, "key3": 999}
    //A two value assignment can be used to check existence of a key.
    value, isKeyPresent := sampleMap["key2"]
    //isKeyPresent will be true if key present in sampleMap
    if isKeyPresent {
        //key exist
        fmt.Println("key present, value =  ", value)
    } else {
        //key does not exist
        fmt.Println("key does not exist")
    }
}
Nik
  • 2,885
  • 2
  • 25
  • 25
Fathah Rehman P
  • 8,401
  • 4
  • 40
  • 42
-4

Example usage: Looping through a slice, for pairMap checking if key exists. It an algorithm to find all pairs that adds to a specific sum.

func findPairs(slice1 []int, sum int) {
    pairMap := make(map[int]int)
    for i, v := range slice1 {
        if valuei, ok := pairMap[v]; ok {
            fmt.Println("Pair Found", i, valuei)
        } else {
            pairMap[sum-v] = i
        }
    }
}
Sumer
  • 2,687
  • 24
  • 24