Skip to Content

入門Goプログラミング Lesson19(マップ)から学んだこと

守備範囲が広いマップ

Pythonで言うところのdictである

マップの宣言

1
map[int]string // intがキーの型、stringが値の型

存在しない値にアクセスした場合は型の0値が返る
intなら0、stringなら""

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {
	legendPlayers := map[int]string{
		10: "Zidane", // 複合リテラル: マップではキーと値がペア
		20: "Recoba", // , 忘れずに!
	}

	player := legendPlayers[10]
	fmt.Printf("ファンタジスタと言えば %s\n", player)

	// 空のマップにアクセスした場合
	fmt.Println(legendPlayers[7])

	legendPlayers[10] = "Ronaldinho" // 上書き
	legendPlayers[5] = "Zidane"      // 新規追加
	fmt.Println(legendPlayers)
}
ファンタジスタと言えば Zidane
                // 空のマップにアクセスした際のstringの場合、結果は何もなくて正解
map[5:Zidane 10:Ronaldinho 20:Recoba]

intだと空のマップにアクセスると0が返ってくる
本当に0の場合と、空で0の場合どう見分けたら良いのか
この場合C.C.が空である

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "fmt"

func main() {
	codeGeass := map[string]int{
		"Zero": 0,
	}

	fmt.Println(codeGeass["Zero"])
	fmt.Println(codeGeass["C.C."])
}
0
0

, okを足して見分けることができる
, foundでも、カンマの後ろに付ける変数はなんでもいい
本当に0であればbool型のtrueが変数に入り、なければfalseが入る

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

func main() {
	codeGeass := map[string]int{
		"Zero": 0,
	}

	// マップにある場合
	if lelouch, ok := codeGeass["Zero"]; ok {
		fmt.Printf("当たり %v %v\n", ok, lelouch)
	} else {
		fmt.Printf("ハズレ %v %v\n", ok, lelouch)
	}

	// マップにない場合
	if lelouch, ok := codeGeass["C.C."]; ok {
		fmt.Printf("当たり %v %v\n", ok, lelouch)
	} else {
		fmt.Printf("ハズレ %v %v\n", ok, lelouch)
	}
}
当たり true 0
ハズレ false 0

マップはコピーされない

同じ基底配列を共有する
片方に変更を加えると、もう片方にも影響がでる
delete関数は要素をマップから削除する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

func main() {
	codeGeass := map[string]string{
		"Zero": "lelouch",
		"C.C.": "不明",
	}

	codeGeass2 := codeGeass
	codeGeass["Zero"] = "Suzaku"

	fmt.Println(codeGeass)
	fmt.Println(codeGeass2)

	delete(codeGeass, "Zero")

	fmt.Println(codeGeass)
	fmt.Println(codeGeass2)
}
map[C.C.:不明 Zero:Suzaku]
map[C.C.:不明 Zero:Suzaku]
map[C.C.:不明]
map[C.C.:不明]

makeでマップを事前に割り当てる

スライスと同様で、マップの初期サイズを指定することにより、
そのマップがあとで大きくなったときマシンの処理を削減できる

1
codeGeass := make(map[string]string, 2)

マップを使って計測する

スライスと、マップ内の文字の出現頻度を求める
この場合はマップが有利

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func main() {
	numbers := []int{
		3, 5, 2, 1, 5, 8, 5, 3, 1, 6, 4, 8, 0,
	}

	filter := make(map[int]int)

	// スライスをマップに突っ込む
	for _, num := range numbers {
		filter[num]++ // 同じキーがでてきたらカウントが増える
	}

	fmt.Println(filter)

	for i, selectNum := range filter {
		fmt.Printf("%d の出現は %d回です\n", i, selectNum)
	}
}
map[0:1 1:2 2:1 3:2 4:1 5:3 6:1 8:2]
8 の出現は 2回です
6 の出現は 1回です
4 の出現は 1回です
0 の出現は 1回です
3 の出現は 2回です
5 の出現は 3回です
2 の出現は 1回です
1 の出現は 2回です

一意の値を持つマップ

セット(集合)は、どの要素も重複しない配列に似たコレクション
Goではsetという名前のものはなく、マップで代用する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
	numbers := []int{
		3, 5, 2, 1, 5, 8, 5, 3, 1, 6, 4, 8, 0,
	}

	// intからboolに変更
	filter := make(map[int]bool)

	// スライスをマップに突っ込む
	for _, num := range numbers {
		filter[num] = true // カウントアップではなく、true入れて2個目来ても上書きするだけ
	}

	fmt.Println(filter)
}
map[0:true 1:true 2:true 3:true 4:true 5:true 6:true 8:true]

重複を取り除いたマップのソートしたい
マップはキーの順序が任意
マップを今度はスライスに戻してソートする必要がある

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
	"fmt"
	"sort"
)

func main() {
	numbers := []int{
		3, 5, 2, 1, 5, 8, 5, 3, 1, 6, 4, 8, 0,
	}

	// intからboolに変更
	filter := make(map[int]bool)

	// スライスをマップに突っ込む
	for _, num := range numbers {
		filter[num] = true // カウントアップではなく、true入れて2個目来ても上書きするだけ
	}

	fmt.Println(filter)

	// ソートしたい場合はマップでは無理だからスライスに戻す
	unique := make([]int, 0, len(filter))
	for t := range filter {
		unique = append(unique, t)
	}
	sort.Ints(unique)
	fmt.Println(unique)
}
[0 1 2 3 4 5 6 8]

まとめ

  • マップの初期化には複合リテラルを使うのが便利
  • 代入または関数呼び出しで渡されたマップは元と同じ基底配列を共有する

感想

goのキーバリューはmapということはわかった