🦔

GoのGenericsで便利関数を作成する

2025/02/24に公開

はじめに

HITOTSU株式会社の河村康治です。
今回はタイトルにもある通り、GoのGenericsを活用した便利関数を紹介します。

Contains

スライス内に特定の要素が含まれるかを検知する関数です。

main.go
package main

import "fmt"

// Contains はスライスに特定の要素が含まれているか確認
func Contains[T comparable](slice []T, target T) bool {
	for _, v := range slice {
		if v == target {
			return true
		}
	}
	return false
}

func main() {
	numbers := []int{1, 2, 3, 4, 5}
	fmt.Println(Contains(numbers, 3))  // true
	fmt.Println(Contains(numbers, 10)) // false

	words := []string{"apple", "banana", "cherry"}
	fmt.Println(Contains(words, "banana")) // true
}
実行結果
~/go/go_generics/sample/contains  (main)$ go run main.go 
true
false
true
スライスと比較する値の型が異なる場合

スライスと比較する値の型が異なる場合は下記のようなコンパイルエラーが発生します

Filter関数

スライス内から特定の条件で絞り込みをかける関数です。

main.go
package main

import "fmt"

// Filter はスライスから条件を満たす要素を抽出
func Filter[T any](slice []T, predicate func(T) bool) []T {
	var result []T
	for _, v := range slice {
		if predicate(v) {
			result = append(result, v)
		}
	}
	return result
}

func main() {
	numbers := []int{1, 2, 3, 4, 5}
	even := Filter(numbers, func(n int) bool { return n%2 == 0 }) // 偶数を抽出
	fmt.Println(even)                                             // [2, 4]

	words := []string{"apple", "banana", "cherry"}
	withA := Filter(words, func(s string) bool { return len(s) > 5 }) // 5文字より長い文字列を抽出
	fmt.Println(withA)                                                // ["banana", "cherry"]
}
実行結果
~/go/go_generics/sample/contains  (main)$ go run main.go
[2 4]
[banana cherry]

Mapの辞書操作

Mapの辞書操作を実施する関数です。KeyやValue値を抽出します

Keyの抽出

main.go
package main

import "fmt"

// Keys はマップから全てのキーを取得
func Keys[K comparable, V any](m map[K]V) []K {
	keys := make([]K, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

func main() {
	m := map[string]int{"Alice": 30, "Bob": 25, "Charlie": 35}
	fmt.Println(Keys(m)) // ["Alice", "Bob", "Charlie"]
}

実行結果
~/go/go_generics/sample/contains  (main)$ go run main.go
[Alice Bob Charlie]

Valueの抽出

main.go
package main

import "fmt"

// Values はマップから全ての値を取得
func Values[K comparable, V any](m map[K]V) []V {
	values := make([]V, 0, len(m))
	for _, v := range m {
		values = append(values, v)
	}
	return values
}

func main() {
	m := map[string]int{"Alice": 30, "Bob": 25, "Charlie": 35}
	fmt.Println(Values(m)) // [30, 25, 35]
}
実行結果
~/go/go_generics/sample/map  (main)$ go run main.go
[30 25 35]

数値操作

Sum関数

main.go
package main

import "fmt"

// Sum は数値スライスの合計を計算
func Sum[T int | float64](slice []T) T {
	var sum T
	for _, v := range slice {
		sum += v
	}
	return sum
}

func main() {
	ints := []int{1, 2, 3, 4, 5}
	fmt.Println(Sum(ints)) // 15

	floats := []float64{1.1, 2.2, 3.3}
	fmt.Println(Sum(floats)) // 6.6
}
実行結果
~/go/go_generics/sample/sum  (main)$ go run main.go
15
6.6

その他操作

Reduce関数

main.go
package main

import "fmt"

// Reduce はスライスを1つの値に集約
func Reduce[T any, U any](slice []T, initial U, accumulator func(U, T) U) U {
	result := initial
	for _, v := range slice {
		result = accumulator(result, v)
	}
	return result
}

func main() {
	numbers := []int{1, 2, 3, 4, 5}
	sum := Reduce(numbers, 0, func(acc int, n int) int { return acc + n })
	fmt.Println(sum) // 15

	words := []string{"Go", "is", "awesome"}
	concat := Reduce(words, "", func(acc string, s string) string { return acc + s + " " })
	fmt.Println(concat) // "Go is awesome "
}
実行結果
~/go/go_generics/sample/reduce  (main)$ go run main.go
15
Go is awesome

最後に

簡単に作れて便利なので一度試してみてください!

HITOTSU株式会社 テックブログ

Discussion