🧱

Go 1.23でmapのキーをソートする

2024/08/15に公開

結論

Go 1.23.0からは、for文一つでmapを順序付きでループできる。

var m map[int]string

for _, k := range slices.Sorted(maps.Keys(m)) {
    fmt.Println("Key:", k, "Value:", m[k])
}

これまでのやり方

Goのmaprangeループでイテレーションする際、キーの順番は保証されておらず、実行するたびに変わります。

var m map[int]string

// 実行するたびに順番が変わる。
for k, v := range m {
    fmt.Println("Key:", k, "Value:", v)
}

順番を保証するには、事前にキーの配列を作成し、それをソートして使います。
例えば、キーの配列をslices.Sort関数でソートし、その配列をrangeループで回します。

var m map[int]string
var keys []int

// ソート済みのキー配列を用意する。
for k := range m {
    keys = append(keys, k)
}
slices.Sort(keys)

// 必ずキーの昇順で表示される。
for _, k := range keys {
    fmt.Println("Key:", k, "Value:", m[k])
}

Go 1.23でのやり方

mapをイテレーションするのにfor文が二つ必要なのは少々気が利いていません。
Go 1.23.0からは、標準ライブラリを使ってよりコンパクトに書けるようになります。

肝となるのは、次の2つの関数です:

maps.Keysslices.Sortedを使うと、キーの昇順で要素を出力するプログラムは次のように書けます。

var m map[int]string

// キーの昇順で出力される。
for _, k := range slices.Sorted(maps.Keys(m)) {
    fmt.Println("Key:", k, "Value:", m[k])
}

キーの降順で要素を出力したい場合は、slices.SortedFuncが使えます。

var m map[int]string

// キーの降順で出力される。
for _, k := range slices.SortedFunc(maps.Keys(m), func (a, b int) int {
    return cmp.Compare(b, a)
}) {
    fmt.Println("Key:", k, "Value:", m[k])
}

Discussion