Go/マップ
< 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)になり、ok
はfalse
になります。 - エントリーの追加
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
は複合リテラルのフォーマットを表示します。- エントリー数を得る
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
型のレシーバを持ち、マップ操作を実行します。