🔑 Goのジェネリクスを使ってマップからソートされたキーを取得する方法 🔗

Goにおいて、マップ(map)は本質的に順序を持たないデータ構造です。for range ループを使ってマップを反復処理すると、その順序はランダムになります。ジェネリクスが導入される前は、マップのキーを抽出してソートするために、ボイラープレートコードや型ごとの専用関数を記述する必要がありました。

現代のGoのジェネリクスと標準ライブラリパッケージ(cmp および slices)を使えば、クリーンで再利用可能な SortedKeys 関数を驚くほどシンプルかつ簡潔に実装できます。

📑 目次 🔗


💻 モダンな実装(Go 1.21以降) 🔗

package main

import (
	"cmp"
	"slices"
	"fmt"
)

// SortedKeys はマップからすべてのキーを抽出し、ソートされた順序で返します。
// キーの型が順序付け可能(整数、浮動小数点数、文字列)であれば、どのようなマップでも動作します。
func SortedKeys[K cmp.Ordered, V any](m map[K]V) []K {
	r := make([]K, 0, len(m))
	for k := range m {
		r = append(r, k)
	}
	slices.Sort(r)
	return r
}

func main() {
	// 例1:整数型のキー
	intMap := map[int]string{3: "three", 1: "one", 2: "two"}
	fmt.Println(SortedKeys(intMap))
	// Output: [1 2 3]

	// 例2:文字列型のキー
	strMap := map[string]int{"banana": 2, "apple": 4, "cherry": 1}
	fmt.Println(SortedKeys(strMap))
	// Output: [apple banana cherry]
}

⚙️ 仕組みの解説 🔗

  1. ジェネリック型制約(K cmp.Ordered, V any):
    • K cmp.Ordered は、マップのキーを比較演算子(<、>、<=、>=)をサポートする型(整数、浮動小数点数、文字列など)に制限します。
    • V any により、マップの値は任意の型をとることができます(any は interface{} のエイリアスです)。
  2. スライスの事前確保(プリ・アロケーション):
    • make([]K, 0, len(m)) によってマップの要素数と同じ容量(キャパシティ)を持つスライスを事前に確保し、append ループ中に発生するコストの高い動的メモリの再割り当てを回避します。
  3. キーの抽出:
    • for k := range m ループですべてのキーをスライスに収集します。
  4. ソート:
    • slices.Sort(r) は、Goの高度に最適化されたソートアルゴリズムを使って、キーのスライスをインプレース(破壊的)にソートします。

🚀 なぜ2020年の設計ドラフト版より優れているのか 🔗

2020年6月、Goのジェネリクスがまだ設計ドラフト段階だった頃は、sort.Interface を満たすカスタム型を手動で実装する必要がありました。

// Go 1.18以前の設計ドラフト段階のアプローチ(現在は廃止)
type Ordered interface {
	type int, int8, ... // インターフェース内の型リスト
}

type orderedSlice[T Ordered] []T
func (s orderedSlice[T]) Len() int           { return len(s) }
func (s orderedSlice[T]) Less(i, j int) bool { return s[i] < s[j] }
func (s orderedSlice[T]) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

現在のモダンなGoでは、以下の機能が提供されています。


📚 無料電子書籍をダウンロード 🔗

カウエの生き方のオフライン版を入手しましょう——🍏 健康、💰 富、❤️ 愛、🧘 意味を最適化するための実践ガイドです。

カウエの生き方

カウエの生き方

📬 ニュースレターを購読する 🔗

新しいエッセイ、本の要約、より良く生きるための実践ガイドを公開した際にお知らせします。スパムはありません——いつでも配信停止できます。


💬 コメントとディスカッション 🔗

質問、ご意見、提案はありますか?ぜひ下であなたの考えを共有してください。コメント欄は GitHub Discussions(広告なし・スパムなし)を利用しています——下の GitHub でサインイン をクリックしてコメントやリアクションを残してください(無料の GitHub アカウントは1分未満で作成できます)。



最終更新日: 2026/10/11.

免責事項:当サイトのコンテンツは情報提供および教育のみを目的としており、医療、心理、金融、投資、税務、または法律上の助言を構成するものではありません。ここに掲載されている考え、キュレーション、意見はすべて私個人のものであり、雇用主の見解を代表するものではありません。書籍、講演、記事の要約は独立した教育的な解説であり、それぞれの著者の知的財産です。編集、フォーマット、要約の補助としてAIツールを使用する場合がありますが、正確性を期すよう努めているものの誤りが含まれる可能性があります。プライバシー:当サイトでは、任意のニュースレター購読(follow.it)、コメント機能(giscus / GitHub Discussions)、および埋め込みメディア(YouTube)に外部サービスを利用しており、これらのサービスはそれぞれのプライバシーポリシーに従って訪問者のIPアドレスやCookieを処理する場合があります。