web-dev-qa-db-ja.com

マップにGoのキーが含まれているかどうかを確認する方法

私は、地図mを次のように反復できることを知っています、

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

キーを探しますが、マップ内のキーの存在をテストするためのより効率的な方法はありますか?

言語仕様 に答えが見つかりませんでした。

569
grokus

一行答:

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

説明:

Goのifname__ステートメントには、条件と初期化ステートメントの両方を含めることができます。上記の例では両方を使用しています。

  • 2つの変数を初期化します - valname__はマップから "foo"の値を受け取るか、 "zero value"(この場合は空の文字列)を受け取り、okname__はboolを受け取り、 "foo"が実際に存在する場合はtruename__に設定されます。マップ内

  • okname__を評価します。マップ内に "foo"が含まれていた場合はtruename__になります。

"foo"が実際にマップに存在する場合、ifname__ステートメントの本体が実行され、valname__はそのスコープに対してローカルになります。

1082
marketer

編集: 次の答えはGo 1の前です。Go1から始まって、それはもはや正確/有効ではありません。


Goプログラミング言語仕様 に加えて、 Effective Go を読むべきです。 maps の節で、彼らは、とりわけ言っています:

「マップに存在しないキーでマップ値を取得しようとするとプログラムがクラッシュしますが、多重代入を使用して安全に実行する方法があります。」

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

「実際の値を気にせずにマップ内での存在をテストするには、空白の識別子、単純なアンダースコア(_)を使用できます。空白の識別子は、値を無害に破棄して任意の型の値で割り当てまたは宣言できます。マップ内の存在をテストするには、値の通常の変数の代わりに空白の識別子を使用してください。 "

_, present := timeZone[tz]
92
peterSO

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ステートメントを使用すると、value変数とok変数は、if条件内でのみ表示されます。

44
grokus

短い答え

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

これが Go Playgroundでの例 です。

長い答え

Effective GoMaps セクションあたり:

マップ内に存在しないキーを使用してマップ値を取得しようとすると、マップ内のエントリのタイプに対してゼロ値が返されます。たとえば、マップに整数が含まれている場合、存在しないキーを検索すると0が返されます。

時々、足りないエントリとゼロ値を区別する必要があります。 "UTC"のエントリはありますか、それともマップにないため空の文字列ですか?あなたは複数の割り当ての形で区別することができます。

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

明白な理由で、これは「コンマOK」イディオムと呼ばれます。この例では、tzが存在する場合、秒数が適切に設定され、okがtrueになります。そうでなければ、secondsは0に設定され、okはfalseになります。これは、Niceエラーレポートと組み合わせた関数です。

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

実際の値を気にせずにマップ内の存在をテストするには、値の通常の変数の代わりに空白の識別子(_)を使用できます。

_, present := timeZone[tz]
20
Matthew Rankin

他の答えで述べたように、一般的な解決策は インデックス式代入 で使用することです。

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

これは素敵できれいです。ただし、いくつか制限があります。特別な形式の代入でなければなりません。右側の式はマップインデックス式のみで、左側の式リストには2つのオペランドが含まれている必要があります。1つはオペランドに割り当て可能なもので、もう1つはbool値が割り当て可能なものです。この特殊形式のインデックス式の結果の最初の値はキーに関連付けられた値になり、2番目の値は指定されたキーを持つエントリが実際にマップに存在するかどうかを示します(キーがマップに存在する場合)。左側の式リストには、 空白の識別子 のいずれかの結果が必要でない場合も含まれます。

インデックス付きマップ値が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と比較します。

この「テクニック」を使うことにはもう一つの利点があります:あなたはコンパクトな方法で複数のキーの存在をチェックすることができます(あなたはそれを特別な「カンマOK」形式で行うことはできません)。これについての詳細: キーが1つの条件で複数のマップに存在するかどうかを確認します

10
icza

ここより良い方法

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

"インデックス式" の下に記載されています。

特殊形式の代入または初期設定で使用されるmap [K] V型のマップ上の索引式

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

追加の型なしブール値を返します。キーxがマップに存在する場合、okの値はtrueです。それ以外の場合、falseです。

4
mroman
    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を実行してください。本当は存在しませんか?偽

3
Lady_Exotel

この目的のために2値割り当てを使用することができます。下記の私のサンプルプログラムをチェックしてください

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")
    }
}
1
Fathah Rehman P