Goでマップにキーが含まれているかどうかを確認するにはどうすればよいですか?


762

地図を繰り返し処理できることはわかっていますmが、

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

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

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


2
ここにリンクされた仕様で答えを見つける場所があります:golang.org/ref/spec#Index_expressions
nobar

回答:


1474

一行の答え:

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

説明:

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

  • 2つの変数を初期化します- valマップから「foo」の値または「ゼロ値」(この場合は空の文字列)のいずれかokを受け取り、true「foo」が実際にマップに存在する場合に設定されるブール値を受け取ります

  • 評価するokだろう、true「foo」がマップにあった場合

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


2
これは、(peterSOからの他のコメントのように)どのように機能するかをよりよく説明できるかもしれません
Chmouel Boudjnah '29

6
@Kiril var val string = ""は同じままで、val, ok :=そのブロックでのみ表示される同じ名前の新しいローカル変数を作成します。
OneOfOne 2014年

1
素晴らしい答えです、これの複雑さはO(1)だと思いますか?
Mheni

1
@Mheni、私はここで少し遅れていることを知っていますが、この質問では、検索の複雑さについて説明します。ほとんどの場合、償却後の複雑さはO(1)ですが、その質問に対する回答を読む価値はあります。
2018年

70
Pythonの構文と比較すると、このような混乱する構文if key in dictです。
Pranjal Mittal

130

Goプログラミング言語仕様に加えて、Effective Goもお読みください。地図のセクションで、彼らはとりわけ、

マップに存在しないキーを使用してマップ値をフェッチしようとすると、マップ内のエントリのタイプのゼロ値が返されます。たとえば、マップに整数が含まれている場合、存在しないキーを検索すると0が返されます。セットは、値タイプboolのマップとして実装できます。マップエントリを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]

明らかな理由により、これは「コンマok」イディオムと呼ばれます。この例では、tzが存在する場合、秒が適切に設定され、okがtrueになります。そうでない場合、秒はゼロに設定され、okはfalseになります。これは、素晴らしいエラーレポートと一緒にまとめる関数です:

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によって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と、and ok変数はif条件内でのみ表示されます。


21
キーが存在するかどうかに本当に興味があり、値に関心がない場合は、を使用できます_, ok := dict["baz"]; ok_一部には、一時変数を作成するのではなく、離れた値をスローします。
Matthew Crumley、2010年

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]

明らかな理由により、これは「コンマok」イディオムと呼ばれます。この例では、tzが存在する場合、秒が適切に設定され、okがtrueになります。そうでない場合、秒はゼロに設定され、okはfalseになります。これは、素晴らしいエラーレポートと一緒にまとめる関数です:

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。この特別なフォームの結果の最初の値はキーに関連付けられた値になり、2番目の値は指定されたキーを持つマップにエントリが実際にあるかどうかを示します(キーがマップに存在する場合)。結果の1つが不要な場合は、左側の式リストに空白の識別子が含まれる場合もあります。

インデックス付きのマップ値に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つの条件で存在するかどうかを確認する

存在しないキーでインデックスを作成するときに値タイプのゼロ値を取得すると、値を持つマップをセット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

関連項目を参照:一意の文字列を含む配列を作成するにはどうすればよいですか?


1
何がT入っvar v, ok T = a[x]てるの?されていないokブール値でなければなりませんか?
Kokizzu

2
@Kokizzu変数宣言の一般的な形式です。まずはマップはタイプであろう場合にのみ(コンパイル)に働くだろうと思うかもしれないmap[bool]boolTあるboolが、マップのタイプがある場合、それはまた、作品map[interface{}]boolTありますinterface{}。さらに、それはまた持つカスタムタイプで動作bool上のすべてを参照して、基になる型としてゴー遊び場。したがって、その形式はに代わる複数のタイプで有効であるためT、それが将軍Tが使用される理由です。タイプはok型なしboolを割り当てることができる任意のものにすることができます。
icza

7

ここより良い方法

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

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)

次に、maps.goを実行します。本当は存在しませんか?偽


intの必要性をなくします
Lady_Exotel

貢献に感謝しますが、私は現在の回答が問題を十分カバーしていると思います。ここであなたが言っていることから、あなたの答えは、Goタイプの質問のセットを実装する最良の方法に、より適合します。
tomasz 2015

_, ok = m["somestring"]する必要があります=_, ok := m["somestring"]
Elroy Jetson '20

3

これは、下記載され、「インデックス式」

特殊なフォームの割り当てまたは初期化で使用されるmap [K] Vタイプのマップのインデックス式

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

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


1

この目的で、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")
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.