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


Answers:


1469

一线回答:

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

说明:

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

  • 初始化两个变量- val将接收来自映射的“ foo”值或“零值”(在这种情况下为空字符串),ok并将接收一个布尔值,true如果在映射中实际存在“ foo” ,则该布尔值将设置为

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

如果映射中确实存在“ foo”,if则将执行该语句的主体,并且该主体在该val作用域内是本地的。


2
可以更好地解释它是如何工作的(就像peterSO的其他评论一样)
Chmouel Boudjnah 2014年

6
@Kiril var val string = ""将保持不变,val, ok :=使用仅在该块中可见的相同名称创建一个新的局部变量。
OneOfOne 2014年

1
好的答案,你会说这的复杂度是O(1)吗?
Mheni

1
@Mheni,我知道我来晚了,但是这个问题讨论了go中的查找复杂性。大多数情况下,摊销的复杂度为O(1),但值得阅读该问题的答案。
世始

70
与python的语法相比,这种语法令人困惑if key in dict
Pranjal Mittal '18

128

除了Go编程语言规范之外,您还应该阅读Effective Go。他们在地图部分中说:

尝试使用映射中不存在的键来获取映射值时,将为映射中的条目类型返回零值。例如,如果映射包含整数,则查找不存在的键将返回0。可以将集合实现为值类型为bool的映射。将映射项设置为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]

由于明显的原因,这被称为“逗号可以”的成语。在此示例中,如果存在tz,则将适当设置秒数,并且ok为true;否则,将为true。如果不是,则将秒设置为零,确定为假。这是一个将其与错误报告结合在一起的函数:

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

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

_, present := timeZone[tz]

57

在“ 坚果”电子邮件列表中进行搜索,找到了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条件内部可见。


20
如果您真的只是对密钥是否存在感兴趣,并且不关心该值,则可以使用_, ok := dict["baz"]; ok。该_部件会丢弃该值,而不是创建一个临时变量。
马修·克鲁姆利

26

简短答案

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

这是Go Playground中示例

更长的答案

根据有效围棋地图部分:

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

有时您需要从零值中区分出缺失的条目。是否存在“ UTC”条目,或者是空字符串,因为它根本不在地图中?您可以采用多种分配形式进行区分。

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

由于明显的原因,这被称为“逗号可以”的成语。在此示例中,如果存在tz,则将适当设置秒数,并且ok为true;否则,将为true。如果不是,则将秒设置为零,确定为假。这是一个将其与错误报告结合在一起的函数:

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

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

_, present := timeZone[tz]

13

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

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

这很干净。但是它有一些限制:它必须是特殊形式的赋值。右侧表达式必须仅是map索引表达式,左侧表达式列表必须恰好包含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

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] != "")

输出(在Go Playground上尝试):

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

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

使用此“技术”也有另一个优点:您可以以紧凑的方式检查多个键的存在(您不能使用特殊的“逗号确定”形式来执行此操作)。关于此的更多信息:在一种情况下检查密钥是否存在于多个地图中

使用不存在的键建立索引时,获取值类型的零值还使我们能够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

请参阅相关内容:如何创建包含唯一字符串的数组?


1
是什么Tvar v, ok T = a[x]?不一定ok是布尔吗?
科科祖

2
@Kokizzu这是变量声明的一般形式。起初,我们可能认为只有map为type map[bool]boolTis 才可以工作(编译)bool,但是map为type map[interface{}]boolTis 时也可以工作(编译)interface{}。此外,它还适用于具有bool作为基础类型的自定义类型,请参见Go Playground上的全部内容。因此,由于该形式对于替换为的多个类型都是有效T的,所以才使用常规T。类型ok可以是任何东西到了无类型的bool可分配。
icza

7

这里更好的方法

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

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

3
    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存在吗?真的不存在吗?假


摆脱了对int的需求
Lady_Exotel

感谢您的贡献,但是我认为当前的答案很好地涵盖了这个问题。从您在这里所说的话,您的答案将更适合实现Go类型问题中的set最佳方式
tomasz 2015年

_, ok = m["somestring"]应该是=_, ok := m["somestring"]
Elroy Jetson

3

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

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

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

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


1

为此可以使用两个值的分配。请在下面检查我的示例程序

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")
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.