如何在Go中检查map是否包含键?

我知道我可以迭代地图m

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

并寻找密钥,但是有没有更有效的方法来测试密钥在地图中的存在?

1138920 次浏览

坚果电子邮件列表上搜索,找到了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条件中可见。

一行回答:

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

说明:

Go中的if语句可以同时包含条件和初始化语句。上面的示例同时使用:

  • 初始化两个变量-val将从map接收“foo”的值或“零值”(在本例中为空字符串),ok将接收一个bool,如果“foo”实际上存在于map中,则该bool将设置为true

  • 计算ok,如果“foo”在地图中,则为true

如果“foo”确实存在于map中,if语句的主体将被执行,val将是该范围的本地。

除了Go编程语言规范,你应该阅读有效围棋。在地图的部分,他们说,除其他事项外:

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

attended := map[string]bool{"Ann": true,"Joe": true,...}
if attended[person] { // will be false if person is not in the mapfmt.Println(person, "was at the meeting")}

有时您需要将丢失的条目与零值区分开来。有没有“UTC”的条目,或者是0,因为它不在地图上你可以用多重赋值的形式来区分。

var seconds intvar ok boolseconds, ok = timeZone[tz]

由于显而易见的原因,这被称为“逗号ok”习语例如,如果tz存在,秒将被适当地设置并确定将为真;如果不是,秒将设置为零,ok将为这里有一个函数把它和一个不错的错误放在一起报告:

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

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

_, present := timeZone[tz]
    var empty struct{}var ok boolvar 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存在吗?真的不存在?false

简短回答

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

示例

这是一个在Go Playground的例子

更长的答案

根据有效围棋地图部分:

尝试使用映射中不存在的键获取映射值将返回映射中条目类型的零值。例如,如果映射包含整数,则查找不存在的键将返回0。

有时你需要将缺失的条目与零值区分开来。是否有“UTC”的条目,或者它是空字符串,因为它根本不在映射中?您可以使用多重赋值的形式进行区分。

var seconds intvar ok boolseconds, ok = timeZone[tz]

出于显而易见的原因,这被称为“逗号ok”习语。在这个例子中,如果tz存在,秒将被适当设置,ok将为真;如果没有,秒将被设置为零,ok将为假。这是一个将它与一个漂亮的错误报告放在一起的函数:

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

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

_, present := timeZone[tz]

正如其他答案所指出的,一般的解决方案是在特殊形式的分配中使用索引表达式

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

这很好也很干净。但是它有一些限制:它必须是一个特殊形式的赋值。右侧表达式只能是map索引表达式,左侧表达式列表必须包含两个操作数,第一个操作数的值类型是可赋值的,第二个操作数的值是bool。这个特殊形式的结果的第一个值将是与键关联的值,第二个值将告诉是否在map中实际存在一个具有给定键的条目(如果键存在于map中)。如果不需要其中一个结果,左侧表达式列表也可能包含空白标识符

重要的是要知道,如果索引映射值为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上试一试。

因此,如果我们知道我们没有在映射中使用零值,我们可以利用这一点。

例如,如果值类型是string,并且我们知道我们从不在map中存储值为空字符串的条目(string类型为零值),我们还可以通过比较索引表达式的非特殊形式与零值来测试键是否在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: trueKey 1 exists: trueKey 2 exists: false

在实践中,有很多情况下我们不会将零值存储在map中,所以这可以经常使用。例如接口和函数类型的零值nil,我们通常不会将其存储在map中。所以测试键是否在map中可以通过将其与nil进行比较来实现。

使用这种“技术”还有另一个好处:您可以以紧凑的方式检查多个键的存在(您不能使用特殊的“逗号确定”形式来做到这一点)。更多关于这个:在一个条件下检查key是否存在于多个map中

在使用不存在的键进行索引时获取值类型的零值还允许我们方便地将具有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

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

它在"索引表达式"下被提及。

赋值中使用的map[K]V类型的map a上的索引表达式或初始化特殊形式

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

产生一个额外的无类型布尔值。ok的值为true,如果键x存在于map中,否则为false。

两个值的赋值可以用于此目的。请检查我下面的示例程序

package main
import ("fmt")
func main() {//creating a map with 3 key-value pairssampleMap := 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 sampleMapif isKeyPresent {//key existfmt.Println("key present, value =  ", value)} else {//key does not existfmt.Println("key does not exist")}}
    var d map[string]stringvalue, ok := d["key"]if ok {fmt.Println("Key Present ", value)} else {fmt.Println(" Key Not Present ")}

看看这段代码

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

示例用法:循环通过切片,用于对Map检查键是否存在。这是一个算法,找到所有对,增加到一个特定的总和。

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