【问题标题】:How to check if a map contains a key in Go?如何检查地图是否包含 Go 中的键?
【发布时间】:2011-01-04 06:10:02
【问题描述】:

我知道我可以通过 m 遍历地图,

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

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

我在language spec 中找不到答案。

【问题讨论】:

标签: dictionary go go-map


【解决方案1】:
推荐的答案 Go Language

一行回答:

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

说明:

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

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

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

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

【讨论】:

  • 如果在if val,ok ... 上方的一行声明了一个val 会发生什么:var val string = ""
  • @Kiril var val string = "" 将保持不变,val, ok := 创建一个同名的新局部变量,该变量仅在该块中可见。
  • @Mheni,我知道我来晚了,但this question 讨论了 go 中的查找复杂性。大多数情况下,amortized complexity 是 O(1),但值得阅读该问题的答案。
  • 请注意,如果您有一个“与”条件,则它必须在密钥存在之后进行。有没有办法解决这个问题?
  • @PrashantSaraswat 不,如果你想让 AND 出现在前面,你需要分解 if 语句。
【解决方案2】:

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

尝试使用不存在于 map 将返回映射中条目类型的零值。 例如,如果地图包含整数,则查找不存在的 键将返回 0。集合可以实现为具有值类型的映射 布尔。将映射条目设置为 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 将是 错误的。这是一个将它与一个很好的错误组合在一起的函数 报告:

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

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

_, present := timeZone[tz]

【讨论】:

【解决方案3】:

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_ 部分将值丢弃而不是创建临时变量。
【解决方案4】:

简答

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

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

_, present := timeZone[tz]

【讨论】:

    【解决方案5】:

    正如其他答案所指出的,一般的解决方案是在特殊形式的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 值的映射方便地用作。例如:

    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?

    【讨论】:

    • T 中的var v, ok T = a[x] 是什么? ok 不是必须是布尔值吗?
    • @Kokizzu 这就是变量声明的一般形式。起初,我们可能认为只有当 map 的类型为 map[bool]boolTbool 时它才有效(编译),但如果 map 的类型为 map[interface{}]boolT 为 @987654352,它也有效@;此外,它还适用于以bool 作为基础类型的自定义类型,请参阅Go Playground 上的所有内容。因此,由于该形式对替换T 的多种类型有效,这就是使用一般T 的原因。 ok 的类型可以是任何可以分配无类型 bool 的内容。
    【解决方案6】:
        var d map[string]string
        value, ok := d["key"]
        if ok {
            fmt.Println("Key Present ", value)
        } else {
            fmt.Println(" Key Not Present ")
        }
    

    【讨论】:

      【解决方案7】:

      看看这个sn-p的代码

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

      【讨论】:

        【解决方案8】:
            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 一些字符串存在吗?真的 不存在?假的

        【讨论】:

        • 摆脱对 int 的需要
        • 感谢您的贡献,但我认为当前的答案很好地涵盖了这个问题。从您在这里所说的来看,您的答案将更适合 在 Go 中实现集合的最佳方式类型的问题。
        • _, ok = m["somestring"] 应该是=_, ok := m["somestring"]
        【解决方案9】:

        "Index expressions" 下提到。

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

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

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

        【讨论】:

          【解决方案10】:

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

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

          【讨论】:

            【解决方案11】:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-11-10
              • 1970-01-01
              • 1970-01-01
              • 2012-09-28
              • 1970-01-01
              相关资源
              最近更新 更多