Làm cách nào để kiểm tra xem bản đồ có chứa khóa trong Go không?


762

Tôi biết tôi có thể lặp lại trên bản đồ mbằng cách,

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

và tìm kiếm một khóa nhưng có cách nào hiệu quả hơn để kiểm tra sự tồn tại của khóa trong bản đồ không?

Tôi không thể tìm thấy câu trả lời trong thông số ngôn ngữ .


2
Đây là nơi để tìm câu trả lời trong thông số được liên kết: golang.org/ref/spec#Index_expressions
nobar

Câu trả lời:


1474

Một dòng trả lời:

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

Giải trình:

ifcác câu lệnh trong Go có thể bao gồm cả một điều kiện và một câu lệnh khởi tạo. Ví dụ trên sử dụng cả hai:

  • khởi tạo hai biến - valsẽ nhận giá trị "foo" từ bản đồ hoặc "giá trị 0" (trong trường hợp này là chuỗi rỗng) và oksẽ nhận được một bool sẽ được đặt thành truenếu "foo" thực sự có trong bản đồ

  • đánh giá ok, sẽ là truenếu "foo" có trong bản đồ

Nếu "foo" thực sự có mặt trên bản đồ, phần thân của ifcâu lệnh sẽ được thực thi và valsẽ cục bộ trong phạm vi đó.


2
Điều này có thể được giải thích rõ hơn về cách thức hoạt động của nó (giống như nhận xét khác từ peterSO)
Chmouel Boudjnah

6
@Kiril var val string = ""sẽ giữ nguyên, val, ok :=tạo ra một biến cục bộ mới có cùng tên chỉ có thể nhìn thấy trong khối đó.
OneOfOne

1
câu trả lời tuyệt vời, bạn sẽ nói sự phức tạp của điều này là O (1) ??
Mheni

1
@Mheni, tôi biết tôi đến hơi muộn ở đây, nhưng câu hỏi này thảo luận về sự phức tạp của việc tra cứu. Hầu hết thời gian độ phức tạp khấu hao là O (1) nhưng đáng để đọc câu trả lời cho câu hỏi đó.
3ocene

70
Đó là một cú pháp khó hiểu so với python if key in dict.
Pranjal Găngal

130

Ngoài Đặc tả ngôn ngữ lập trình Go , bạn nên đọc Go hiệu quả . Trong phần trên bản đồ , họ nói, trong số những thứ khác:

Cố gắng tìm nạp một giá trị bản đồ bằng một khóa không có trong bản đồ sẽ trả về giá trị 0 cho loại mục nhập trong bản đồ. Chẳng hạn, nếu bản đồ chứa các số nguyên, tra cứu khóa không tồn tại sẽ trả về 0. Một tập hợp có thể được thực hiện dưới dạng bản đồ với bool loại giá trị. Đặt mục nhập bản đồ thành true để đặt giá trị trong tập hợp, sau đó kiểm tra nó bằng cách lập chỉ mục đơn giản.

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

Đôi khi bạn cần phân biệt một mục bị thiếu với giá trị bằng không. Có một mục cho "UTC" hay là 0 vì nó hoàn toàn không có trong bản đồ? Bạn có thể phân biệt với một hình thức nhiều bài tập.

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

Vì những lý do rõ ràng, nó được gọi là thành ngữ và dấu phẩy ok. Trong ví dụ này, nếu tz có mặt, giây sẽ được đặt một cách thích hợp và ok sẽ là đúng; nếu không, giây sẽ được đặt thành 0 và ok sẽ sai. Đây là một chức năng kết hợp nó với một báo cáo lỗi hay:

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

Để kiểm tra sự hiện diện trong bản đồ mà không phải lo lắng về giá trị thực tế, bạn có thể sử dụng mã định danh trống (_) thay cho biến thông thường cho giá trị.

_, present := timeZone[tz]

57

Tìm kiếm trên danh sách email go-nut và tìm thấy một giải pháp được đăng bởi Peter Froehlich vào ngày 15/11/2009.

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

Hoặc, gọn hơn,

if value, ok := dict["baz"]; ok {
    fmt.Println("value: ", value)
} else {
    fmt.Println("key not found")
}

Lưu ý, sử dụng hình thức này của ifcâu lệnh, các biến valueokchỉ được hiển thị bên trong các ifđiều kiện.


21
Nếu bạn thực sự quan tâm đến việc khóa có tồn tại hay không và không quan tâm đến giá trị, bạn có thể sử dụng _, ok := dict["baz"]; ok. Phần _ném giá trị đi thay vì tạo một biến tạm thời.
Matthew Crumley

26

Câu trả lời ngắn

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

Thí dụ

Đây là một ví dụ tại Sân chơi Go .

Câu trả lời dài hơn

Theo phần Bản đồ của Đi hiệu quả :

Cố gắng tìm nạp một giá trị bản đồ bằng một khóa không có trong bản đồ sẽ trả về giá trị 0 cho loại mục nhập trong bản đồ. Chẳng hạn, nếu bản đồ chứa các số nguyên, tra cứu khóa không tồn tại sẽ trả về 0.

Đôi khi bạn cần phân biệt một mục bị thiếu với giá trị bằng không. Có một mục cho "UTC" hay đó là chuỗi trống vì nó hoàn toàn không có trong bản đồ? Bạn có thể phân biệt với một hình thức nhiều bài tập.

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

Vì những lý do rõ ràng, nó được gọi là thành ngữ và dấu phẩy ok. Trong ví dụ này, nếu tz có mặt, giây sẽ được đặt một cách thích hợp và ok sẽ là đúng; nếu không, giây sẽ được đặt thành 0 và ok sẽ sai. Đây là một chức năng kết hợp nó với một báo cáo lỗi hay:

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

Để kiểm tra sự hiện diện trong bản đồ mà không phải lo lắng về giá trị thực tế, bạn có thể sử dụng mã định danh trống (_) thay cho biến thông thường cho giá trị.

_, present := timeZone[tz]

13

Như đã lưu ý bởi các câu trả lời khác, giải pháp chung là sử dụng biểu thức chỉ mục trong một phép gán của dạng đặc biệt:

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

Điều này là tốt đẹp và sạch sẽ. Nó có một số hạn chế mặc dù: nó phải là một sự phân công của hình thức đặc biệt. Biểu thức phía bên phải chỉ là biểu thức chỉ mục bản đồ và danh sách biểu thức bên trái phải chứa chính xác 2 toán hạng, đầu tiên là loại giá trị được gán và một boolgiá trị có thể gán được. Giá trị đầu tiên của kết quả của hình thức đặc biệt này sẽ là giá trị được liên kết với khóa và giá trị thứ hai sẽ cho biết nếu thực sự có một mục trong bản đồ với khóa đã cho (nếu khóa tồn tại trong bản đồ). Danh sách biểu thức phía bên trái cũng có thể chứa định danh trống nếu không cần một trong các kết quả.

Điều quan trọng cần biết là nếu giá trị bản đồ được lập chỉ mục có nilhoặc không chứa khóa, biểu thức chỉ mục sẽ ước tính giá trị 0 của loại giá trị của bản đồ. Ví dụ:

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

Hãy thử nó trên Sân chơi Go .

Vì vậy, nếu chúng tôi biết rằng chúng tôi không sử dụng giá trị 0 trong bản đồ của mình, chúng tôi có thể tận dụng lợi thế này.

Ví dụ: nếu loại giá trị là stringvà chúng tôi biết rằng chúng tôi không bao giờ lưu trữ các mục trong bản đồ trong đó giá trị là chuỗi trống (giá trị 0 cho stringloại), chúng tôi cũng có thể kiểm tra xem khóa có trong bản đồ hay không bằng cách so sánh không đặc biệt dạng của biểu thức chỉ số (kết quả của) đến giá trị 0:

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

Đầu ra (thử trên Sân chơi Go ):

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

Trong thực tế, có nhiều trường hợp chúng ta không lưu trữ giá trị 0 trong bản đồ, vì vậy điều này có thể được sử dụng khá thường xuyên. Ví dụ: giao diện và loại chức năng có giá trị bằng 0 nil, mà chúng ta thường không lưu trữ trong bản đồ. Vì vậy, kiểm tra nếu một khóa trong bản đồ có thể đạt được bằng cách so sánh nó với nil.

Sử dụng "kỹ thuật" này cũng có một lợi thế khác: bạn có thể kiểm tra sự tồn tại của nhiều khóa theo cách nhỏ gọn (bạn không thể làm điều đó với biểu mẫu "dấu phẩy ok" đặc biệt). Thông tin thêm về điều này: Kiểm tra xem khóa có tồn tại trong nhiều bản đồ trong một điều kiện không

Lấy giá trị 0 của loại giá trị khi lập chỉ mục với khóa không tồn tại cũng cho phép chúng tôi sử dụng bản đồ với boolcác giá trị thuận tiện dưới dạng bộ . Ví dụ:

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

Nó xuất ra (thử trên Sân chơi Go ):

Contains 'one': true
'two' is in the set
'three' is not in the set

Xem liên quan: Làm thế nào tôi có thể tạo một mảng có chứa các chuỗi duy nhất?


1
những gì là Ttrong var v, ok T = a[x]? không okphải là bool?
Kokizzu

2
@Kokizzu Đó là hình thức chung của khai báo biến. Lúc đầu, chúng tôi có thể nghĩ rằng nó sẽ chỉ hoạt động (biên dịch) nếu bản đồ sẽ là loại map[bool]boolTbool, nhưng nó cũng hoạt động nếu bản đồ là loại map[interface{}]boolTinterface{}; hơn nữa, nó cũng hoạt động với các loại tùy chỉnh có boolkiểu cơ bản, xem tất cả trên Sân chơi Go . Vì vậy, vì hình thức đó là hợp lệ với nhiều loại được thay thế cho T, đó là lý do tại sao cái chung Tđược sử dụng. Loại okcó thể là bất cứ thứ gì mà một unypedbool có thể được chỉ định.
icza


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)

Sau đó, đi chạy maps.go somestring tồn tại? đúng không tồn tại? sai



Cảm ơn sự đóng góp, nhưng tôi nghĩ rằng các câu trả lời hiện tại bao gồm các câu hỏi tốt. Từ những gì bạn đang nói ở đây, câu trả lời của bạn sẽ phù hợp hơn với cách triển khai tốt nhất được đặt trong loại câu hỏi Go .
tomasz

_, ok = m["somestring"]nên là=_, ok := m["somestring"]
Elroy Jetson

3

Nó được đề cập dưới "Biểu thức chỉ mục" .

Biểu thức chỉ mục trên bản đồ a bản đồ loại [K] V được sử dụng trong gán hoặc khởi tạo biểu mẫu đặc biệt

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

mang lại một giá trị boolean chưa được bổ sung. Giá trị của ok là đúng nếu khóa x có trong bản đồ và ngược lại là sai.


1

Một phép gán hai giá trị có thể được sử dụng cho mục đích này. Vui lòng kiểm tra chương trình mẫu của tôi dưới đây

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")
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.