如何检查地图中是否包含密钥?
我知道我可以遍历一个地图m,
for k, v := range m { ... }
并寻找一个关键,但有一个更有效的方式来testing一个密钥的存在在地图中? 谢谢。 我无法在语言规范中find答案。
一行答案:
if val, ok := dict["foo"]; ok { //do something here }
说明:
if
Go中的语句可以同时包含一个条件和一个初始化语句。 上面的例子使用了两个:
-
初始化两个variables –
val
会从map中获得“foo”的值或者是一个“零值”(在这个例子中是空string),ok
会收到一个bool,如果实际存在“foo”在地图上 -
评估
ok
,如果地图中有“foo”,则为true
如果地图中确实存在“foo”,则if
语句的主体将被执行, val
将在该范围内。
编辑:以下答案在Go 1之前。从Go 1开始,它不再是准确/有效的。
除了Go编程语言规范之外 ,您还应该阅读Effective Go 。 在地图部分,他们说,除其他外:
“尝试使用地图中不存在的键获取地图值将导致程序崩溃,但是有一种方法可以安全地使用多个任务。”
var seconds int var ok bool seconds, ok = timeZone[tz]
“为了testing地图中的存在而不必担心实际值,可以使用空白标识符,简单的下划线(_)。空白标识符可以被赋值或声明为任何types的任何值,并且该值被无害地丢弃。要在地图上testing出现位置,请使用空白标识符代替值的常用variables。
_, present := timeZone[tz]
在疯狂的电子邮件列表上search ,find了Peter Froehlich于11/15/2009发布的解决scheme。
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
语句的这种forms, value
和ok
variables只在if
条件中可见。
简答
_, exists := timeZone[tz] // Just checks for key existence val, exists := timeZone[tz] // Checks for key existence and retrieves the value
例
这是Go游乐场的一个例子 。
更长的答案
根据Effective Go的地图部分:
尝试使用地图中不存在的键获取地图值时,将返回地图中条目types的零值。 例如,如果地图包含整数,查找一个不存在的键将返回0。
有时你需要从零值中区分一个缺失的条目。 有没有“UTC”的条目,或者是空string,因为它根本不在地图中? 您可以使用多种分配forms进行区分。
var seconds int var ok bool seconds, ok = timeZone[tz]
出于显而易见的原因,这被称为“逗号确定”成语。 在这个例子中,如果tz存在,秒将被适当地设置并且ok将是真的; 如果没有,秒将被设置为零,ok将是假的。 这里有一个函数,把它和一个不错的错误报告放在一起:
func offset(tz string) int { if seconds, ok := timeZone[tz]; ok { return seconds } log.Println("unknown time zone:", tz) return 0 }
要testing映射中的出现而不用担心实际值,可以使用空白标识符(_)代替该值的常用variables。
_, present := timeZone[tz]
正如其他答案所指出的,一般的解决scheme是在特殊forms的赋值中使用索引expression式 :
v, ok = a[x] v, ok := a[x] var v, ok = a[x] var v, ok T = a[x]
这是很好,干净。 但是它有一些限制:它必须是特殊forms的赋值。 右侧expression式只能是映射索引expression式,而左侧expression式列表必须包含刚好有两个操作数,第一个值types是可赋值的,第二个操作数赋值给bool
值。 这个特殊forms的索引expression式的结果的第一个值将是与键关联的值,第二个值将告诉在给定键(如果键存在于该映射中)的情况下地图上是否存在实际的条目。 如果其中一个结果不需要,则左侧expression式列表还可以包含空白标识符 。
重要的是要知道,如果索引映射值是nil
或不包含键,则索引expression式将计算为映射值types的零值 。 举个例子:
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上试试吧。
所以如果我们知道我们不使用地图中的零值,我们可以利用这个优势。
例如,如果值types是string
,并且我们知道我们不会在映射中存储值为空string( string
types为零值)的条目,那么我们还可以通过比较非映射值来testing键是否在映射中 – 索引expression式(的结果)的特殊forms为零值:
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
在实践中,有许多情况下,我们不把零值存储在地图中,所以这可以经常使用。 例如,接口和函数types的零值nil
,我们经常不存储在地图中。 所以testing一个关键是否在地图上可以通过比较它来达到nil
。
使用这个“技术”还有另外一个好处:你可以用一个紧凑的方式检查多个键的存在(你不能用特殊的“逗号”表格来做)。 更多关于这个: 检查一个条件中是否存在多个映射关键字
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存在? 真的不存在? 假
这里更好的方法
if _, ok := dict["foo"]; ok { //do something here }
只是使用
if len(m) == 0 { ... }