Go/マップ

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go
ナビゲーションに移動 検索に移動

Map型[編集]

Goでは連想配列(ハッシュテーブル)のことをmapと呼びます。

マップの生成と様々な操作[編集]

マップの生成と様々な操作
package main

import "fmt"

func main() {
	m := map[string]int{
		"アップルジュース": 150,
		"牛乳":       100,
		"サイダー":     180,
	}
	printEntry(m, "牛乳")
	printEntry(m, "レモンジュース")
	m["レモンジュース"] = 110
	printEntry(m, "レモンジュース")
	delete(m, "牛乳")
	printEntry(m, "牛乳")
	delete(m, "コーヒー")

	fmt.Println("m =", m)
	fmt.Printf("%%v = %v\n", m)
	fmt.Printf("%%+v = %+v\n", m)
	fmt.Printf("%%#v = %#v\n", m)

	fmt.Println("len(m) =", len(m))

	for k, v := range m {
		fmt.Printf("for::%v は %v 円です。\n", k, v)
	}
}

func printEntry(m map[string]int, key string) {
	if v, ok := m[key]; ok {
		fmt.Printf("%v は %v 円です。\n", key, v)
	} else {
		fmt.Printf("%v の取り扱いは有りません。\n", key)
	}
}
実行結果
牛乳 は 100 円です。
レモンジュース の取り扱いは有りません。
レモンジュース は 110 円です。
牛乳 の取り扱いは有りません。
m = map[アップルジュース:150 サイダー:180 レモンジュース:110]
%v = map[アップルジュース:150 サイダー:180 レモンジュース:110]
%+v = map[アップルジュース:150 サイダー:180 レモンジュース:110]
%#v = map[string]int{"アップルジュース":150, "サイダー":180, "レモンジュース":110}
len(m) = 3
for::アップルジュース は 150 円です。
for::サイダー は 180 円です。
for::レモンジュース は 110 円です。
解説
	m := map[string]int{
		"アップルジュース": 150,
		"牛乳":       100,
		"サイダー":     180,
	}
stringをキーにintを値に持つMap型の変数 m を宣言し、3エントリーのリストで初期化しています。
値の参照
func printEntry(m map[string]int, key string) {
	if v, ok := m[key]; ok {
		fmt.Printf("%v は %v 円です。\n", key, v)
	} else {
		fmt.Printf("%v の取り扱いは有りません。\n", key)
	}
}
マップからキーに対する値を参照するときのイディオムです。
頻回に使うので関数にしました。
Goでは関数の前方参照を関数宣言なしにできます(そもそも関数定義を伴わない関数宣言はGoにはありません)。
単にv := m[key]とするとkeyに対応するエントリがなかった場合、vには値の初期値が返ります(この場合はintの初期値の 0 で nil ではありません)
このため2つ目の戻り値(この場合は ok)を評価してエントリーの有無を調べる必要があります。
エントリーの追加
	printEntry(m, "レモンジュース")
	m["レモンジュース"] = 110
	printEntry(m, "レモンジュース")
実行結果
レモンジュース の取り扱いは有りません。
レモンジュース は 110 円です。
元はエントリーがなかった状態から、新たなキーと値を与えると、エントリーを追加できます。
エントリーの削除
	delete(m, "牛乳")
	printEntry(m, "牛乳")
実行結果
牛乳 の取り扱いは有りません。
エントリーの削除は組込み関数 delete を使います。
存在しないキーを使った削除
	delete(m, "コーヒー")
存在しないキーを使って削除しても何も起こりません(panic も投げられません)。
マップなるごと文字列化
	fmt.Println("m =", m)
	fmt.Printf("%%v = %v\n", m)
	fmt.Printf("%%+v = %+v\n", m)
	fmt.Printf("%%#v = %#v\n", m)
実行結果
m = map[アップルジュース:150 サイダー:180 レモンジュース:110]
%v = map[アップルジュース:150 サイダー:180 レモンジュース:110]
%+v = map[アップルジュース:150 サイダー:180 レモンジュース:110]
%#v = map[string]int{"アップルジュース":150, "サイダー":180, "レモンジュース":110}
fmt.Printf の %v は fmt.Println と同じフォーマットです。
%+v も同じです(%+vは、構造体の場合フィールド名も表示します)
%#v は、複合リテラルのフォーマットです
エントリー数を得る
	fmt.Println("len(m) =", len(m))
エントリー数は組込み関数 len を使って得ます。
イテレーション
	for k, v := range m {
		fmt.Printf("for::%v は %v 円です。\n", k, v)
	}
マップもコレクションの一種なので for 文の range 句の対象にできます。
サンプルコードの通りキーと値をペアごとに繰り返します。

様々な操作をメソッド化[編集]

単価表を1つの型として定義して、様々な操作をメソッドにしてみました。 メソッドチェーンができるよう、自分自身を戻値にしました。

様々な操作をメソッド化
package main

import "fmt"

type PriceTable map[string]int

func main() {
	PriceTable{
		"アップルジュース": 150,
		"牛乳":       100,
		"サイダー":     180,
	}.
		print("牛乳").
		print("レモンジュース").
		set("レモンジュース", 110).
		print("レモンジュース").
		remove("牛乳").
		print("牛乳").
		remove("コーヒー").
		each(func(k string, v int) {
			fmt.Printf(" each::%v は %v 円です。\n", k, v)
		})
}

func (pt PriceTable) get(key string) int {
	return pt[key]
}

func (pt PriceTable) set(key string, v int) PriceTable {
	pt[key] = v
	return pt
}

func (pt PriceTable) print(key string) PriceTable {
	if v, ok := pt[key]; ok {
		fmt.Printf("%v は %v 円です。\n", key, v)
	} else {
		fmt.Printf("%v の取り扱いは有りません。\n", key)
	}
	return pt
}

func (pt PriceTable) remove(key string) PriceTable {
	delete(pt, key)
	return pt
}

func (pt PriceTable) len() int {
	return len(pt)
}

func (pt PriceTable) each(f func(k string, v int)) PriceTable {
	for k, v := range pt {
		f(k, v)
	}
	return pt
}
実行結果
牛乳 は 100 円です。
レモンジュース の取り扱いは有りません。
レモンジュース は 110 円です。
牛乳 の取り扱いは有りません。
 each::アップルジュース は 150 円です。
 each::サイダー は 180 円です。
 each::レモンジュース は 110 円です。
解説
型定義
type PriceTable map[string]int
メソッドは(組込み型ではなく)定義された型にしか定義できないので map[string]int の別名 PriceTableを用意します。
複合リテラルとしてインスタンス化
	PriceTable{
		"アップルジュース": 150,
		"牛乳":       100,
		"サイダー":     180,
	}.
最後の行の行末の .(ピリオド)がポイントでメソッド(あるいはフィールド)が続く事を表します(ここに . がないと ; が自動的に挿入されてしまう)。
メソッドチェイン
	    print("牛乳").
		print("レモンジュース").
		set("レモンジュース", 110).
		print("レモンジュース").
		remove("牛乳").
		print("牛乳").
		remove("コーヒー").
		each(func(k string, v int) {
			fmt.Printf(" each::%v は %v 円です。\n", k, v)
		})
様々な操作を(チェイン可能な)メソッドにしました。
each は繰り返し処理を抽象化しました。

脚註[編集]