web-dev-qa-db-ja.com

Golangのネストされたマップ

func main() {
    var data = map[string]string{}
    data["a"] = "x"
    data["b"] = "x"
    data["c"] = "x"
    fmt.Println(data)
}

走ります。

func main() {
    var data = map[string][]string{}
    data["a"] = append(data["a"], "x")
    data["b"] = append(data["b"], "x")
    data["c"] = append(data["c"], "x")
    fmt.Println(data)
}

また、実行されます。

func main() {
    var w = map[string]string{}
    var data = map[string]map[string]string{}
    w["w"] = "x"
    data["a"] = w
    data["b"] = w
    data["c"] = w
    fmt.Println(data)
}

また走ります!

func main() {
    var data = map[string]map[string]string{}
    data["a"]["w"] = "x"
    data["b"]["w"] = "x"
    data["c"]["w"] = "x"
    fmt.Println(data)
}

しかし、それは失敗します!?

Goのネストされたマップに問題はありますか?または、ネストされたマップの複数のブラケットのサポートはありませんか?

33

マップタイプの ゼロ値nilです。まだ初期化されていません。 nilマップに値を保存することはできません。これはランタイムパニックです。

最後の例では、(外部)dataマップを初期化しますが、エントリはありません。 data["a"]のようにインデックスを作成すると、"a"キーを持つエントリがまだないため、インデックスを作成すると、マップのnilである値タイプのゼロ値が返されます。したがって、data["a"]["w"]に割り当てようとすると、実行時のパニックが発生します。

要素を格納する前に、まずマップを初期化する必要があります。次に例を示します。

var data = map[string]map[string]string{}

data["a"] = map[string]string{}
data["b"] = make(map[string]string)
data["c"] = make(map[string]string)

data["a"]["w"] = "x"
data["b"]["w"] = "x"
data["c"]["w"] = "x"
fmt.Println(data)

出力( Go Playground で試してください):

map[a:map[w:x] b:map[w:x] c:map[w:x]]

マップタイプの変数を宣言し、 複合リテラルvar data = map[string]string{}のように)で初期化すると、これも初期化としてカウントされることに注意してください。

複合リテラルを使用してネストされたマップを初期化することもできます。

var data = map[string]map[string]string{
    "a": map[string]string{},
    "b": map[string]string{},
    "c": map[string]string{},
}

data["a"]["w"] = "x"
data["b"]["w"] = "x"
data["c"]["w"] = "x"
fmt.Println(data)

出力は同じです。 Go Playground で試してください。

44
icza

Iczaの答えに加えて。マップの初期化は 短い形式 で書くことができます:

var data = map[string]map[string]string{
    "a": map[string]string{
        "w": "x"},
    "b": map[string]string{
        "w": "x"},
    "c": map[string]string{
        "w": "x"},
    "d": map[string]string{},
}
fmt.Println(data)

出力は同じです。 Go Playground で試してください。空のマップでのマッピングを示すために追加されたキー「d」。

3
Vladimir Filin

この質問に対する最も簡単な答えは、前述のようにネストされたマップを初期化することですが、アクセスパターンに応じて別の潜在的なオプションがあります。真に階層的なマップシステムが必要な場合は、前の回答で十分です。ただし、単に複数のファセットを使用してマップ内の値を検索する必要がある場合は、先に進んでください!

マップがstructsをキーとして使用することは完全に受け入れられます(実際、 comparable であるものはすべて使用できます)。したがって、 Golangブログのこの例 のような構造キーを持つ単一のマップを使用できます。これは、国ごとのページヒットを追跡するヒットカウンターです。

type Key struct {
  Path, Country string
}

hits := make(map[Key]int)

// set: Vietnamese person visiting the home page
hits[Key{"/", "vn"}]++

// get: see how many Chinese persons read the spec
n := hits[Key{"/ref/spec", "cn"}]

私はこのようなマップを十分に頻繁に見ていませんが、代わりに多くの人々が最初にネストされたバリアントに手を伸ばします。

1
Dominic Barnes