如何在Go语言中检查一个map是否包含某个键?

1338

我知道可以使用以下方式遍历映射 m

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

查找键,但是否有更有效的方法来测试地图中的键是否存在?


6
以下是在链接规范中查找答案的位置:https://golang.org/ref/spec#Index_expressions - Brent Bradburn
11个回答

2565

以下是如何检查一个映射是否包含某个键的方法。

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

这段代码初始化了两个变量。如果映射中存在“foo”键,则val的值为对应值,否则为“零值”(在此情况下为空字符串)。ok是一个布尔型变量,如果键存在,则设置为true

如果需要,可以将其简化为一行。

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

在 Go 语言中,你可以在 if 语句的条件之前放置一个初始化语句(注意分号)。这样做的结果是,valok 的作用域将仅限于 if 语句的主体部分,在那里使用它们非常有帮助。


22
@Kiril var val string = "" 会保持不变,而 val, ok := 则会创建一个同名的新局部变量,只在该代码块中可见。 - OneOfOne
3
如果 _, ok := dict["foo"]; !ok {} - Nearoo
2
如果你只想检查某个键是否存在,你可以使用空白标识符(_)代替val。像这样:if _, ok := dict["foo"]; ok { //在这里执行某些操作 } - Emeka Augustine
1
在同一个if语句中,是否可以检查两个不同映射中是否存在相同的键? - Neniel
2
Go语言中使用(x, err)(x, ok)这两种形式有点令人困惑。 - BallpointBen
显示剩余7条评论

203
除了《Go编程语言规范》之外,你还应该阅读《Effective Go》。在maps的章节中,他们说:

尝试使用map中不存在的键检索值将返回map条目类型的零值。例如,如果map包含整数,则查找不存在的键将返回0。可以将集合实现为值类型为bool的map。将map条目设置为true以将值放入集合中,然后通过简单索引进行测试。

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")
}
有时候你需要区分一个缺失的条目和零值。 "UTC"是否有条目,还是因为根本不在地图中而为0?你可以用一种多重赋值形式来区分它们。
var seconds int
var ok bool
seconds, ok = timeZone[tz]

由于显而易见的原因,这被称为“逗号 ok”惯用语。在这个例子中,如果 tz 存在,seconds 将被适当设置,ok 将为 true;如果不存在,seconds 将被设置为零,ok 将为 false。以下是将其与良好的错误报告组合在一起的函数:

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

如果你不需要关心实际值,只是测试是否存在于映射中,那么可以使用空白标识符(_)代替通常用于值的变量。

_, present := timeZone[tz]

1
https://go.dev/play/p/X4liolwTsVL 没有工作。 - ar-siddiqui
@ar-siddiqui:peterSO的想法是在使用之前初始化“person”,但是这里有一个完整的工作示例:https://go.dev/play/p/_51LQmwicuO - BMDan
这适用于原始类型,但是一旦你有了map[somePrimitive]someStruct{},它就不起作用了,你需要进一步使用布尔语句才能让go工作,参见示例:https://go.dev/play/p/LVvS0ojOJpI - fullStackChris

114

go-nuts 邮件列表 进行搜索,找到了 Peter Froehlich 在 2009 年 11 月 15 日发布的解决方案。

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")
        }
}

更加紧凑的写法是:

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

注意,使用这种形式的if语句,valueok变量只在if条件中可见。


31
如果你只是想知道键是否存在而不关心值,可以使用 _, ok := dict["baz"]; ok_ 部分会舍弃值而不创建临时变量。 - Matthew Crumley

55

简短回答

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

例子

这里有一个在 Go Playground 中的例子

更长的回答

根据Effective Go中的 Maps 部分:

如果使用一个不存在于 map 中的 key 获取 map 的值,将返回该 map 条目类型的零值。例如,如果 map 包含整数,则查找不存在的 key 将返回 0。

有时需要区分缺少条目和零值。对于“UTC”是否有条目,还是因为根本不在 map 中而是空字符串?您可以使用一种多重赋值的形式进行区分。

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

出于明显的原因,这被称为“comma ok”习语。在此示例中,如果存在tz,则将适当设置秒,并将ok设置为true;如果不存在,则将秒设置为零,并将ok设置为false。这是一个将其与漂亮的错误报告结合起来的函数:

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

为了测试是否存在于地图中,而不必担心实际值,可以使用空白标识符(_)代替通常用于值的变量。

_, present := timeZone[tz]

41

看一下这段代码片段

nameMap := make(map[string]int)
nameMap["river"] = 33
v ,exist := nameMap["river"]
if exist {
    fmt.Println("exist ",v)
}

28
var d map[string]string
value, ok := d["key"]
if ok {
    fmt.Println("Key Present ", value)
} else {
    fmt.Println(" Key Not Present ")
}

28

如其他答案所述,通用解决方案是在特殊形式的赋值中使用索引表达式

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

这是一个简洁明了的语句。但它有一些限制: 它必须是特定形式的赋值语句,右侧表达式只能是映射索引表达式,左侧表达式列表必须恰好包含两个操作数,第一个操作数的值类型可赋值,第二个操作数的值类型必须为bool。此特殊形式的结果的第一个值将是与键相关联的值,第二个值将告诉是否实际上存在具有给定键的映射条目(如果该键存在于映射中)。如果左侧表达式列表中的一个结果不需要,则还可以包含空标识符

重要的是要知道,如果索引映射值为nil或不包含键,则索引表达式将求值为映射值类型的零值。例如:

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

请试用Go Playground
如果我们知道在map中不会使用零值,我们可以利用这一点。
例如,如果值类型为string,并且我们知道我们从未将值为空字符串(string类型的零值)存储到map中,我们也可以通过比较索引表达式的非特殊形式与零值来测试键是否在map中:
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] != "")

输出(在Go Playground上尝试):

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

实际上,有许多情况我们不会在映射中存储零值,因此这种方法经常被使用。例如,接口和函数类型具有零值nil,我们通常不会将其存储在映射中。因此,通过将其与nil进行比较,可以测试键是否存在于映射中。
使用这种“技巧”还有另一个优点:您可以以紧凑的方式检查多个键的存在性(使用特殊的“逗号ok”形式无法实现)。更多信息请参见:在一个条件中检查多个映射中的键是否存在 当使用不存在的键索引时获取值类型的零值,也允许我们方便地将映射与bool值一起用作集合。例如:
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")
}

它输出(在 Go Playground上试一试):
Contains 'one': true
'two' is in the set
'three' is not in the set

相关阅读:如何创建一个包含唯一字符串的数组?


2
var v, ok T = a[x] 中的 T 是什么?难道 ok 必须是布尔值吗? - Kokizzu
3
这是变量声明的通用形式。起初我们可能认为它只能在地图类型为 map [bool] boolTbool 的情况下工作(编译),但如果地图类型是 map [interface{}] boolTinterface {},那么它也可以工作;此外,它还可以使用具有 bool 作为基础类型的自定义类型,详见Go Playground。所以由于该形式适用于多个类型替换为 T,因此使用了一般的 Tok 的类型可以是任何可以分配未命名的 bool 的东西。 - icza

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)

然后,运行maps.go文件。 字符串somestring存在吗?是true。 不存在吗?是false。


摆脱了int的需要 - Lady_Exotel
感谢您的贡献,但我认为当前的答案已经很好地回答了这个问题。从您在这里所说的内容来看,您的回答更适合于“Go语言中实现集合的最佳方式”这类问题。 - tomasz
_, ok = m["somestring"] should be =_, ok := m["somestring"] - Elroy Jetson

5

这个提及在"索引表达式"下。

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.


4
可以使用两个值的赋值语句来实现这个目的。请查看下面的示例程序:
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")
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接