Go のスライスの応用パターン(フィルタ・変換・削除)
実際の開発では、スライスをフィルタリングしたり、要素を変換したり、特定の要素を削除したりする場面が頻繁にあります。Go でよく使われるイディオムを紹介します。
フィルタパターン
条件に合う要素だけを抽出する基本パターンです。
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// 偶数だけを抽出
evens := []int{}
for _, n := range numbers {
if n%2 == 0 {
evens = append(evens, n)
}
}
fmt.Println(evens) // [2 4 6 8 10]パフォーマンスを意識する場合は、あらかじめ容量を確保しておくと効率的です。
evens := make([]int, 0, len(numbers))
for _, n := range numbers {
if n%2 == 0 {
evens = append(evens, n)
}
}マップ(変換)パターン
各要素を変換して新しいスライスを作るパターンです。
numbers := []int{1, 2, 3, 4, 5}
// 各要素を2倍にする
doubled := make([]int, len(numbers))
for i, n := range numbers {
doubled[i] = n * 2
}
fmt.Println(doubled) // [2 4 6 8 10]変換後の要素数が変わらない場合は、make であらかじめ長さを指定しておくと、append を使わずに済みます。
要素の削除(インデックス指定)
特定のインデックスの要素を削除するには、append とスライシングを組み合わせます。
numbers := []int{0, 1, 2, 3, 4, 5}
i := 2 // 削除するインデックス
numbers = append(numbers[:i], numbers[i+1:]...)
fmt.Println(numbers) // [0 1 3 4 5]この方法は順序を保持しますが、削除位置より後ろの要素をすべて移動するため、大きなスライスでは遅くなります。
要素の削除(順序を保持しない)
順序が重要でない場合は、末尾の要素と入れ替えてから末尾を削る方法が高速です。
numbers := []int{0, 1, 2, 3, 4, 5}
i := 2 // 削除するインデックス
numbers[i] = numbers[len(numbers)-1]
numbers = numbers[:len(numbers)-1]
fmt.Println(numbers) // [0 1 5 3 4]要素の移動が 1 回で済むため、大きなスライスでも効率的です。
条件に合う要素を削除
条件に合う要素をすべて削除するには、フィルタパターンの逆を使います。
numbers := []int{1, 2, 3, 4, 5, 6}
// 偶数を削除(奇数だけ残す)
result := numbers[:0] // 同じ背後の配列を再利用
for _, n := range numbers {
if n%2 != 0 {
result = append(result, n)
}
}
fmt.Println(result) // [1 3 5]numbers[:0] で長さ 0 のスライスを作り、同じ背後の配列を再利用することで、メモリ割り当てを減らせます。
重複の除去
スライスから重複を除去するには、マップを使って既出の要素を追跡します。
numbers := []int{1, 2, 2, 3, 3, 3, 4, 5, 5}
seen := make(map[int]bool)
unique := []int{}
for _, n := range numbers {
if !seen[n] {
seen[n] = true
unique = append(unique, n)
}
}
fmt.Println(unique) // [1 2 3 4 5]この方法は元の順序を保持します。順序が不要で、単に重複を除きたいだけならマップのキーとして使う方法もあります。
逆順にする
スライスを逆順にするには、先頭と末尾から要素を入れ替えていきます。
numbers := []int{1, 2, 3, 4, 5}
for i, j := 0, len(numbers)-1; i < j; i, j = i+1, j-1 {
numbers[i], numbers[j] = numbers[j], numbers[i]
}
fmt.Println(numbers) // [5 4 3 2 1]要素の挿入
特定の位置に要素を挿入するには、append を組み合わせます。
numbers := []int{0, 1, 2, 4, 5}
i := 3 // 挿入位置
v := 3 // 挿入する値
numbers = append(numbers[:i], append([]int{v}, numbers[i:]...)...)
fmt.Println(numbers) // [0 1 2 3 4 5]この方法は一時的なスライスを作成するため、パフォーマンスが重要な場合は別の方法を検討してください。
// より効率的な挿入
numbers := []int{0, 1, 2, 4, 5}
i := 3
v := 3
numbers = append(numbers, 0) // 末尾に空き要素を追加
copy(numbers[i+1:], numbers[i:]) // i以降を1つ後ろにずらす
numbers[i] = v // 挿入
fmt.Println(numbers) // [0 1 2 3 4 5]これらのパターンを覚えておくと、日常的なスライス操作を効率よく書けるようになります。