จะตรวจสอบว่าแผนที่มีรหัสใน Go ได้อย่างไร?


761

ฉันรู้ว่าฉันสามารถทำซ้ำแผนที่mโดย

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

และมองหากุญแจ แต่มีวิธีที่มีประสิทธิภาพมากกว่าในการทดสอบการมีอยู่ของกุญแจในแผนที่หรือไม่?

ฉันไม่สามารถหาคำตอบได้ในส่วนภาษาสเปค


2
ที่นี่เพื่อค้นหาคำตอบในข้อมูลจำเพาะที่เชื่อมโยง: golang.org/ref/spec#Index_expressions
nobar

คำตอบ:


1472

คำตอบหนึ่งบรรทัด:

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

คำอธิบาย:

ifคำสั่งใน Go สามารถรวมทั้งเงื่อนไขและคำสั่งการเริ่มต้น ตัวอย่างข้างต้นใช้ทั้ง:

  • เริ่มต้นตัวแปรสองตัว - valจะได้รับค่า "foo" จากแผนที่หรือ "ค่าศูนย์" (ในกรณีนี้คือสตริงว่าง) และokจะได้รับค่าบูลที่จะถูกตั้งค่าtrueหาก "foo" แสดงอยู่ในแผนที่

  • ประเมินokซึ่งจะเป็นtrueถ้า "foo" อยู่ในแผนที่

หาก "foo" มีอยู่จริงในแผนที่เนื้อความของifคำสั่งจะถูกดำเนินการและvalจะอยู่ในขอบเขตนั้น


2
สิ่งนี้สามารถอธิบายได้ดีขึ้นว่ามันทำงานอย่างไร (เช่นความคิดเห็นอื่นจาก peterSO)
Chmouel Boudjnah

6
@Kiril var val string = ""จะยังคงเหมือนเดิมval, ok :=สร้างตัวแปรท้องถิ่นใหม่ที่มีชื่อเดียวกับที่มองเห็นได้ในบล็อกนั้น
OneOfOne

1
คำตอบที่ดีคุณจะบอกว่าความซับซ้อนของเรื่องนี้คือ O (1)?
Mheni

1
@Meni ฉันรู้ว่าฉันมาสายนิดหน่อย แต่คำถามนี้พูดถึงความซับซ้อนในการค้นหา ส่วนใหญ่เวลาของความซับซ้อนที่ถูกตัดจำหน่ายคือ O (1) แต่มันก็คุ้มค่าที่จะอ่านคำตอบของคำถามนั้น
3ocene

70
if key in dictไวยากรณ์สับสนดังกล่าวเมื่อเทียบกับของงูหลาม
Pranjal Mittal

130

นอกเหนือจากการที่ไปเขียนโปรแกรมภาษาข้อมูลจำเพาะคุณควรอ่านที่มีประสิทธิภาพไป ในส่วนบนแผนที่พวกเขาพูดในสิ่งอื่น ๆ :

ความพยายามในการดึงค่าแผนที่ด้วยรหัสที่ไม่มีอยู่ในแผนที่จะคืนค่าศูนย์สำหรับประเภทของรายการในแผนที่ ตัวอย่างเช่นหากแผนที่มีจำนวนเต็มการค้นหาคีย์ที่ไม่มีอยู่จะส่งคืน 0 ชุดสามารถดำเนินการเป็นแผนที่ที่มีค่าประเภทบูล ตั้งค่ารายการแผนที่เป็นจริงเพื่อใส่ค่าในชุดแล้วทดสอบโดยการทำดัชนีอย่างง่าย

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 วินาทีจะถูกตั้งค่าอย่างเหมาะสมและตกลงจะเป็นจริง ถ้าไม่ใช่วินาทีจะถูกตั้งค่าเป็นศูนย์และตกลงจะเป็นเท็จ นี่คือฟังก์ชันที่รวมเข้ากับรายงานข้อผิดพลาดที่ดี:

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

ในการทดสอบการมีอยู่ในแผนที่โดยไม่ต้องกังวลเกี่ยวกับค่าจริงคุณสามารถใช้ตัวระบุที่ว่างเปล่า (_) แทนตัวแปรปกติสำหรับค่า

_, present := timeZone[tz]

57

ค้นหาในรายชื่ออีเมล go-nutsและพบวิธีแก้ไขปัญหาที่โพสต์โดย Peter Froehlich เมื่อวันที่ 11/15/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")
        }
}

หรือมากกว่าดาน

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

หมายเหตุการใช้รูปแบบของifคำสั่งนี้valueและokตัวแปรจะมองเห็นได้เฉพาะภายในifเงื่อนไข


21
_, ok := dict["baz"]; okหากคุณจริงๆมีความสนใจเพียงในไม่ว่าจะเป็นกุญแจสำคัญในการมีอยู่หรือไม่และไม่สนใจเกี่ยวกับค่าที่คุณสามารถใช้ _ส่วนพ่นค่าออกไปแทนการสร้างตัวแปรชั่วคราว
Matthew Crumley

26

คำตอบสั้น ๆ

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

ตัวอย่าง

นี่คือตัวอย่างที่ไปสนามเด็กเล่น

คำตอบอีกต่อไป

ตามส่วนแผนที่ของการใช้งานที่มีประสิทธิภาพ :

ความพยายามในการดึงค่าแผนที่ด้วยรหัสที่ไม่มีอยู่ในแผนที่จะคืนค่าศูนย์สำหรับประเภทของรายการในแผนที่ ตัวอย่างเช่นหากแผนที่มีจำนวนเต็มการค้นหาคีย์ที่ไม่มีอยู่จะส่งคืนค่า 0

บางครั้งคุณต้องแยกความแตกต่างของรายการที่หายไปจากค่าศูนย์ มีรายการสำหรับ "UTC" หรือว่าเป็นสตริงว่างเพราะมันไม่ได้อยู่ในแผนที่เลย? คุณสามารถแยกแยะด้วยรูปแบบของการมอบหมายหลาย

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

ด้วยเหตุผลที่ชัดเจนสิ่งนี้เรียกว่าสำนวน“ comma ok” ในตัวอย่างนี้ถ้ามี tz วินาทีจะถูกตั้งค่าอย่างเหมาะสมและตกลงจะเป็นจริง ถ้าไม่ใช่วินาทีจะถูกตั้งค่าเป็นศูนย์และตกลงจะเป็นเท็จ นี่คือฟังก์ชันที่รวมเข้ากับรายงานข้อผิดพลาดที่ดี:

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

ในการทดสอบการมีอยู่ในแผนที่โดยไม่ต้องกังวลเกี่ยวกับค่าจริงคุณสามารถใช้ตัวระบุที่ว่างเปล่า (_) แทนตัวแปรปกติสำหรับค่า

_, present := timeZone[tz]

13

ตามที่ระบุไว้โดยคำตอบอื่น ๆ การแก้ปัญหาทั่วไปคือการใช้การแสดงออกของดัชนีในการกำหนดรูปแบบพิเศษ:

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

ลองใช้ในสนามเด็กเล่นไป

ดังนั้นหากเรารู้ว่าเราไม่ได้ใช้ค่าศูนย์ในแผนที่ของเราเราสามารถใช้ประโยชน์จากสิ่งนี้

ตัวอย่างเช่นหากประเภทค่าคือ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 ):

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

ในทางปฏิบัติมีหลายกรณีที่เราไม่เก็บค่า zero-value ไว้ในแผนที่ดังนั้นจึงสามารถใช้งานได้บ่อยครั้ง ตัวอย่างเช่นอินเทอร์เฟซและประเภทฟังก์ชั่นมีค่าเป็นศูนย์nilซึ่งเรามักจะไม่เก็บไว้ในแผนที่ nilดังนั้นการทดสอบถ้าคีย์อยู่ในแผนที่สามารถทำได้โดยการเปรียบเทียบกับ

การใช้ "เทคนิค" นี้มีข้อดีอีกอย่างเช่นกัน: คุณสามารถตรวจสอบการมีอยู่ของปุ่มหลายปุ่มได้ในแบบกะทัดรัด (คุณไม่สามารถทำได้ด้วยแบบฟอร์ม "comma 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 ):

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

ดูที่เกี่ยวข้อง: ฉันจะสร้างอาร์เรย์ที่มีสตริงที่ไม่ซ้ำได้อย่างไร


1
สิ่งที่อยู่Tในvar v, ok T = a[x]? ไม่okต้องเป็นคนโง่
Kokizzu

2
@ Kokizzu นั่นคือรูปแบบทั่วไปของการประกาศตัวแปร ตอนแรกเราอาจจะคิดว่ามันจะทำงาน (รวบรวม) ถ้าแผนที่จะเป็นชนิดmap[bool]boolและTเป็นboolแต่ก็ยังทำงานหากแผนที่เป็นชนิดmap[interface{}]boolและTเป็นinterface{}; ยิ่งไปกว่านั้นมันยังทำงานร่วมกับประเภทที่กำหนดเองต้องboolเป็นชนิดพื้นฐานดูทั้งหมดบนไปสนามเด็กเล่น ดังนั้นเนื่องจากรูปแบบนั้นใช้ได้กับการทดแทนหลายประเภทTนั่นจึงเป็นเหตุผลที่Tใช้งานทั่วไป ประเภทของokสามารถเป็นอะไรก็ได้ที่สามารถกำหนดชนิดuntypedboolได้
icza



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)

ถ้างั้นไปเรียกใช้ maps.go มีอยู่ไหม จริงไม่ได้อยู่หรือ เท็จ


กำจัดความต้องการ int
Lady_Exotel

ขอบคุณสำหรับการสนับสนุน แต่ฉันคิดว่าคำตอบปัจจุบันครอบคลุมคำถามได้ดี จากสิ่งที่คุณพูดที่นี่คำตอบของคุณจะเหมาะกับวิธีที่ดีที่สุดในการนำชุดคำถามไปใช้
tomasz

_, ok = m["somestring"]ควรเป็น=_, ok := m["somestring"]
Elroy Jetson

3

มันถูกกล่าวถึงภายใต้"การแสดงออกดัชนี"

นิพจน์ดัชนีบนแผนที่ชนิดแผนที่ [K] V ที่ใช้ในการกำหนดหรือการกำหนดค่าเริ่มต้นของรูปแบบพิเศษ

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

ให้ค่าบูลีนที่ไม่ได้พิมพ์เพิ่มเติม ค่าของ ok เป็นจริงถ้าคีย์ x มีอยู่ในแผนที่และเท็จเป็นอย่างอื่น


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")
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.