ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

我在 language spec 中找不到答案。

以下是在链接规范中找到答案的地方:golang.org/ref/spec#Index_expressions

K
Kevin Burke

一行回复:

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

解释:

if Go 中的语句可以同时包含条件和初始化语句。上面的示例同时使用了这两种方法:

初始化两个变量 - val 将从映射中接收“foo”的值或“零值”(在本例中为空字符串),并且 ok 将接收一个布尔值,如果“foo”实际存在,则该值将设置为 true在地图上

评估 ok,如果 "foo" 在地图中,这将是真的

如果映射中确实存在“foo”,则将执行 if 语句的主体,并且 val 将位于该范围内。


如果在 if val,ok ... 上方的一行声明了一个 val:var val string = "",会发生什么?
@Kiril var val string = "" 将保持不变,val, ok := 创建一个具有相同名称的新局部变量,该变量仅在该块中可见。
@Mheni,我知道我在这里有点晚了,但是 this question 讨论了 go 中的查找复杂性。大多数情况下,amortized complexity 是 O(1),但值得阅读该问题的答案。
请注意,如果您有一个“与”条件,它必须在密钥存在之后。有没有办法解决?
如果只想检查某个键是否存在,可以使用空白标识符 (_) 代替 val。像这样:if _, ok := dict["foo"]; ok { //do something here }
p
peterSO

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

尝试使用映射中不存在的键获取映射值将返回映射中条目类型的零值。例如,如果映射包含整数,则查找不存在的键将返回 0。集合可以实现为值类型为 bool 的映射。将 map 条目设置为 true 以将值放入集合中,然后通过简单的索引对其进行测试。 Attended := map[string]bool{ "Ann": true, "Joe": true, ... } if Attended[person] { // 如果人不在地图中,则为 false 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 } 要测试地图中的存在而不用担心实际值,您可以使用空白标识符 (_) 代替通常的变量为价值。 _,现在 := timeZone[tz]


g
grokus

go-nuts email list 上搜索并找到了 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 条件内可见。


如果你真的只关心 key 是否存在,而不关心 value,你可以使用 _, ok := dict["baz"]; ok_ 部分将值丢弃而不是创建临时变量。
C
Community

简答

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

例子

这是一个example at the Go Playground

更长的答案

根据 Effective GoMaps 部分:

尝试使用映射中不存在的键获取映射值将返回映射中条目类型的零值。例如,如果映射包含整数,则查找不存在的键将返回 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 } 要测试地图中的存在而不用担心实际值,您可以使用空白标识符 (_) 代替通常的变量为价值。 _,现在 := timeZone[tz]


i
icza

正如其他答案所指出的,一般的解决方案是在特殊形式的 assignment 中使用 index expression

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

这很好很干净。但是它有一些限制:它必须是特殊形式的赋值。右边的表达式只能是映射索引表达式,左边的表达式列表必须正好包含 2 个操作数,第一个是可分配值类型的,第二个是可分配 bool 值的。这种特殊形式的结果的第一个值将是与键关联的值,第二个值将告诉映射中是否确实存在具有给定键的条目(如果键存在于映射中)。如果不需要其中一个结果,则左侧表达式列表也可能包含 blank identifier

重要的是要知道,如果索引映射值为 nil 或不包含键,则索引表达式的计算结果为映射值类型的 zero value。例如:

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”形式来做到这一点)。有关此的更多信息:Check if key exists in multiple maps in one condition

在使用不存在的键进行索引时获取值类型的零值也允许我们方便地将具有 bool 值的映射用作 sets。例如:

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

查看相关:How can I create an array that contains unique strings?


var v, ok T = a[x] 中的 T 是什么? ok 不是必须是布尔值吗?
@Kokizzu这是变量声明的一般形式。起初我们可能认为只有当 map 的类型为 map[bool]boolTbool 时它才有效(编译),但它也适用于 map 的类型为 map[interface{}]boolT 为 {7 };此外,它还适用于以 bool 作为基础类型的自定义类型,请参阅 Go Playground 上的所有内容。因此,由于该形式对替换 T 的多种类型有效,这就是使用通用 T 的原因。 ok 的类型可以是可以分配 无类型 bool 的任何内容。
M
Mattia Righetti

看看这段代码

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

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

L
Lady_Exotel
    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 的需要
感谢您的贡献,但我认为当前的答案很好地涵盖了这个问题。从您在这里所说的来看,您的答案将更适合在 Go 类型的问题中实现 set 的最佳方式。
_, ok = m["somestring"] 应该是 =_, ok := m["somestring"]
m
mroman

"Index expressions" 下提到了它。

在特殊形式 v, ok = a[x] v, ok := a[x] var v, ok = a[x] 的赋值或初始化中使用的 map [K]V 类型的映射 a 上的索引表达式产生一个额外的无类型布尔值。如果映射中存在键 x,则 ok 的值为 true,否则为 false。


N
Nik

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

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

S
Sumer

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