1109

我知道我可以通过以下方式遍历地图m

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

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

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

4

11 回答 11

2137

One line answer:

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

Explanation:

if statements in Go can include both a condition and an initialization statement. The example above uses both:

  • initializes two variables - val will receive either the value of "foo" from the map or a "zero value" (in this case the empty string) and ok will receive a bool that will be set to true if "foo" was actually present in the map

  • evaluates ok, which will be true if "foo" was in the map

If "foo" is indeed present in the map, the body of the if statement will be executed and val will be local to that scope.

于 2010-01-12T16:48:48.543 回答
173

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

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

出于显而易见的原因,这被称为“comma 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
}

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

_, present := timeZone[tz]
于 2010-01-12T17:37:55.073 回答
83

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语句,valueandok变量仅在if条件内可见。

于 2010-01-12T16:40:52.053 回答
43

简答

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

例子

这是Go Playground 的一个示例

更长的答案

根据Effective Go的地图部分:

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

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

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

出于显而易见的原因,这被称为“comma 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
}

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

_, present := timeZone[tz]
于 2015-11-09T21:38:00.567 回答
22

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

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

这很好很干净。但是它有一些限制:它必须是特殊形式的赋值。右边的表达式只能是映射索引表达式,左边的表达式列表必须正好包含 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.

使用这种“技术”还有另一个优点:您可以以紧凑的方式检查多个键的存在(您不能使用特殊的“逗号 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

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

于 2017-04-07T08:05:59.897 回答
19
    var d map[string]string
    value, ok := d["key"]
    if ok {
        fmt.Println("Key Present ", value)
    } else {
        fmt.Println(" Key Not Present ")
    }
于 2019-11-05T13:09:14.677 回答
17

看看这段代码

nameMap := make(map[string]int)
nameMap["river"] = 33
v ,exist := nameMap["river"]
if exist {
    fmt.Println("exist ",v)
}
于 2020-11-21T08:41:07.437 回答
4
    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 存在吗?真的不存在吗?错误的

于 2015-07-22T12:19:35.057 回答
3

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

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

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

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

于 2018-06-13T18:54:48.370 回答
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")
    }
}
于 2018-09-21T10:27:31.907 回答
-1

示例用法:循环切片,用于 pairMap 检查键是否存在。它是一种算法,用于查找添加到特定总和的所有对。

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
        }
    }
}
于 2021-10-16T02:27:51.503 回答