如何检查地图中是否包含密钥?

gro*_*kus 666 dictionary go go-map

我知道我可以迭代一张地图m,

for k, v := range m { ... }
Run Code Online (Sandbox Code Playgroud)

并寻找一个键,但有一种更有效的方法来测试一个键在地图中的存在吗?

我在语言规范中找不到答案.

mar*_*ter 1278

一行答案:

if val, ok := dict["foo"]; ok {
    //do something here
}
Run Code Online (Sandbox Code Playgroud)

说明:

ifGo中的语句可以包括条件和初始化语句.上面的例子同时使用:

  • 初始化两个变量 - val将从地图接收"foo"的值或"零值"(在这种情况下为空字符串)ok并将接收将被设置为true如果"foo"实际存在于地图中的bool

  • 评估ok,true如果"foo"在地图中

如果映射中确实存在"foo",则if语句的主体将被执行并且val将是该范围的本地.

  • 与python的`if key in dict`相比,这种令人困惑的语法. (35认同)
  • @Kiril`var val string =""`将保持不变,`val,ok:=`创建一个新的局部变量,其名称与该块中唯一可见的相同. (4认同)
  • @alex `if _, ok := dict["foo"]; !好的{}` (3认同)
  • 这可以更好地解释它是如何工作的(如peterSO的其他评论) (2认同)
  • 如果只想检查某个键是否存在,可以使用空白标识符(_)代替 val。像这样: `if _, ok := dict["foo"]; 好的 { //在这里做点什么 }` (2认同)
  • Go 都使用了 `(x, err)` 和 `(x, ok)`,这有点令人困惑。 (2认同)

pet*_*rSO 114

编辑:以下答案在Go 1之前.从Go 1开始,它不再准确/有效.


除了Go Go Programming Language Specification之外,您还应阅读Effective Go.在地图部分,他们说,除其他外:

"尝试使用地图中不存在的键获取地图值将导致程序崩溃,但有一种方法可以安全地使用多次分配."

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")
}
Run Code Online (Sandbox Code Playgroud)

"要测试地图中的状态而不必担心实际值,可以使用空白标识符,一个简单的下划线(_).可以使用任何类型的任何值分配或声明空白标识符,并且无害地丢弃该值.要测试地图中的状态,请使用空白标识符代替值的常用变量."

var seconds int
var ok bool
seconds, ok = timeZone[tz]
Run Code Online (Sandbox Code Playgroud)


gro*_*kus 50

搜索了疯狂的电子邮件列表,找到了Peter Froehlich于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")
        }
}
Run Code Online (Sandbox Code Playgroud)

或者,更紧凑,

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

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

  • 如果你真的只关心密钥是否存在,并且不关心价值,你可以使用`_,ok:= dict ["baz"]; ok`.`_`部分抛出值而不是创建临时变量. (16认同)

小智 41

看看这段代码

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


Mat*_*kin 23

简答

_, exists := timeZone[tz]    // Just checks for key existence
val, exists := timeZone[tz]  // Checks for key existence and retrieves the value
Run Code Online (Sandbox Code Playgroud)

这是Go Playground的一个例子.

更长的答案

根据Effective Go地图部分:

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

有时您需要将缺失的条目与零值区分开来.是否有"UTC"的条目,或者是空字符串,因为它根本不在地图中?您可以区分多种分配形式.

var seconds int
var ok bool
seconds, ok = timeZone[tz]
Run Code Online (Sandbox Code Playgroud)

由于显而易见的原因,这被称为"逗号确定"成语.在这个例子中,如果存在tz,则将适当地设置秒,并且ok将为真; 如果没有,秒将被设置为零,ok将为false.这是一个函数,它将一个很好的错误报告放在一起:

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

要测试地图中的状态而不必担心实际值,可以使用空白标识符(_)代替值的常用变量.

_, present := timeZone[tz]
Run Code Online (Sandbox Code Playgroud)


icz*_*cza 12

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

v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
var v, ok T = a[x]
Run Code Online (Sandbox Code Playgroud)

这很干净.但它有一些限制:它必须是特殊形式的赋值.右侧表达式必须仅是映射索引表达式,并且左侧表达式列表必须包含正好2个操作数,第一个值类型可赋值,第二个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
Run Code Online (Sandbox Code Playgroud)

Go Playground尝试一下.

因此,如果我们知道我们不在地图中使用零值,我们就可以利用这一点.

例如,如果值类型是string,并且我们知道我们从不在地图中存储值为空字符串的条目(string类型的零值),我们还可以通过比较非特殊值来测试密钥是否在地图中索引表达式(的结果)形式为零值:

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] != "")
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

Key 0 exists: true
Key 1 exists: true
Key 2 exists: false
Run Code Online (Sandbox Code Playgroud)

在实践中,在很多情况下我们不将零值值存储在地图中,因此可以经常使用.例如,接口和函数类型的值为零nil,我们通常不会将其存储在映射中.因此,测试一个键是否在地图中可以通过比较它来实现nil.

使用这种"技术"还有另一个优点:您可以以紧凑的方式检查多个键的存在(您不能使用特殊的"逗号确定"形式).更多相关信息:检查一个条件下多个映射中是否存在密钥

  • `var v, ok T = a[x]` 中的 `T` 是什么?‘ok’ 不是必须是 bool 吗? (2认同)
  • @Kokizzu这是变量声明的一般形式.起初我们可能认为只有地图的类型为`map [bool] bool`和`T`为`bool`才能工作(编译),但如果map的类型为`map [interface {},它也可以工作] bool`和`T`是`interface {}`; 此外,它也适用于以"bool"作为基础类型的自定义类型,请参阅[**Go Playground**](https://play.golang.org/p/lqJa8BZv49).因此,由于该形式在多个类型替换为"T"时有效,这就是使用通用"T"的原因."ok"的类型可以是可以分配**无类型`bool`**的任何类型. (2认同)

小智 12

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


小智 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)
Run Code Online (Sandbox Code Playgroud)

然后,运行maps.go,某个字符串存在吗?真的不存在吗?错误的


Ama*_*yyy 6

这里更好的方式

if _, ok := dict["foo"]; ok {
    //do something here
}
Run Code Online (Sandbox Code Playgroud)


mro*_*man 5

它在“索引表达式”下提到。

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

v, ok = a[x] 
v, ok := a[x] 
var v, ok = a[x]
Run Code Online (Sandbox Code Playgroud)

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