コンテンツにスキップ

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)
	}
}
マップからキーに対する値を参照する際のイディオムです。マップから値を取得する際に、値が存在するかどうかを確認するために ok を使います。存在しないキーの場合、値は int 型の初期値(0)になり、 okfalse になります。
エントリーの追加
	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%vfmt.Println と同じフォーマットです。 %+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 型のレシーバを持ち、マップ操作を実行します。
メソッド
func (pt PriceTable) get(key string) int {
	return pt[key]
}
get メソッドは指定されたキーに対する値を取得します。
エントリーの設定
func (pt PriceTable) set(key string, v int) PriceTable {
	pt[key] = v
	return pt
}
set メソッドは指定されたキーに対する値を設定します。メソッドチェーンを可能にするために、操作後の PriceTable を返します。
エントリーの削除
func (pt PriceTable) remove(key string) PriceTable {
	delete(pt, key)
	return pt
}
remove メソッドは指定されたキーのエントリーを削除します。削除後の PriceTable を返します。
各エントリーの処理
func (pt PriceTable) each(f func(k string, v int)) PriceTable {
	for k, v := range pt {
		f(k, v)
	}
	return pt
}
each メソッドは、マップの全てのエントリーに対して関数 f を適用します。メソッドチェーンを可能にするために、操作後の PriceTable を返します。

練習問題

[編集]

次のような操作が可能な Student 型を定義し、その使用方法を示してください。

1. 学生の名前と年齢を格納できるマップ Student 型を定義する。 2. 学生の情報を追加、取得、削除するメソッドを実装する。 3. 学生の情報を全て出力するメソッドを実装する。

実装例:

package main

import "fmt"

type Student map[string]int

func main() {
	students := Student{
		"田中": 20,
		"鈴木": 22,
	}

	students.add("佐藤", 19).add("山田", 21).printAll().remove("鈴木").printAll()
}

func (s Student) add(name string, age int) Student {
	s[name] = age
	return s
}

func (s Student) remove(name string) Student {
	delete(s, name)
	return s
}

func (s Student) printAll() Student {
	for name, age := range s {
		fmt.Printf("%v さんは %v 歳です。\n", name, age)
	}
	return s
}
Student 型は map[string]int を内部に持ち、学生の名前をキー、年齢を値として管理します。各メソッドは Student 型のレシーバを持ち、マップ操作を実行します。

脚註

[編集]