守備範囲が広いマップ
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
ということはわかった