繁体   English   中英

如何检查地图是否包含 Go 中的键?

[英]How to check if a map contains a key in Go?

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

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

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

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

一行回答:

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

说明:

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

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

  • 评估ok ,如果“foo”在地图中,则为true

如果映射中确实存在“foo”,则将执行if语句的主体,并且val将是该范围的局部变量。

除了The Go Programming Language Specification ,您还应该阅读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 为真; 如果没有,秒将被设置为零,确定将是假的。 这是一个将它与一个很好的错误报告放在一起的函数:

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

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

 _, present := timeZone[tz]

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

简答

_, 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]

出于显而易见的原因,这被称为“逗号确定”习语。 在这个例子中,如果 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]

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

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进行比较来测试键是否在映射中。

使用这种“技术”还有另一个优点:您可以以紧凑的方式检查多个键的存在(使用特殊的“逗号确定”形式无法做到这一点)。 更多相关信息: 检查在一种情况下是否存在多个映射中的键

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

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

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

我知道我可以在地图迭代m的,

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

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

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

    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)

然后, go run maps.go somestring 存在吗? 真的不存在吗? 假的

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

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

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

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

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

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

看看这段代码

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

用法示例:循环切片,用于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
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM