はじめに
Goを書いていて「sliceってもう少し組み込み関数あったら良いのに。。」と思うことありません??
僕はPHPを昔書いていたせいか、結構思います笑(PHPってarrayの組み込み関数豊富なんですよね。。)
もちろんslicesパッケージもあるのですが、ちょっと足りない。。足りないないならば作ってしまおう!
ということで、タイトルの通りGoのslice関数を作ってみました。
みなさんに「確かにちょっと嬉しい!」と思ってもらえたら、僕はめっちゃ嬉しいです!!
目次
環境
Go 1.19
sliceの各要素の数を数える
引数のsliceの各要素の数を数えます。
func CountValueSlice[T comparable](slices []T) map[T]int {
resultMap := map[T]int{}
for _, v := range slices {
resultMap[v] += 1
}
return resultMap
}
例えばこんな感じで重複チェックとかにも使えそうと思ったり。。
s := []string{"test1", "test2", "duplicate", "duplicate"}
m := CountValueSlice(s)
for k, v := range m {
if v > 1 {
fmt.Printf("%vは重複しています。", k) // duplicateは重複しています。
}
}
2つのsliceの差分を算出する
第1引数のsliceから第2引数のsliceを引き算するイメージです。
具体的には第1引数のsliceと第2引数のsliceに重複する要素を、第1引数のsliceから取り除きます。
func DiffSlice[T comparable](slice1, slice2 []T) []T {
diffSlice := []T{}
cmpMap := map[T]int{}
// slice2が各要素が何個あるのかmapに格納
for _, v := range slice2 {
cmpMap[v] += 1
}
// slice2にある要素をslice1にあるか確認して、なければdiffSliceに格納
for _, v := range slice1 {
t, ok := cmpMap[v]
// slice1の要素がslice2になければ、配列に格納
if !ok {
diffSlice = append(diffSlice, v)
continue
}
if t == 1 {
delete(cmpMap, v)
} else {
cmpMap[v] -= 1
}
}
return diffSlice
}
実際に処理するとこんな感じ。
func main() {
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{1, 3, 5, 6}
d := slices.DiffSlice(slice1, slice2)
fmt.Println(d) // [2, 4]
}
複数のsliceをmergeする
2つのsliceをマージするだけなら、append関数で行けるのですが複数のsliceの場合はできないので作ってみました!
ただ、複数のsliceをマージしたい時があるかは別ですが笑
func MergeSlice[T any](slices ...[]T) []T {
resultSlice := []T{}
for _, v := range slices {
resultSlice = append(resultSlice, v...)
}
return resultSlice
}
実際に動かすとこんな感じ。
まあこれはそんなに使う場面少ないかもですね笑
func main() {
slice1 := []string{"a", "b"}
slice2 := []string{"c", "d"}
slice3 := []string{"e", "f"}
slice4 := []string{"g", "h"}
ms := slices.MergeSlice(slice1, slice2, slice3, slice4)
fmt.Println(ms) // [a, b, c, d, e, f, g, h]
}
sliceの要素をフィルタリングする
第2引数の関数によって、フィルタリングします。
func FilterSlice[T any](slice []T, fn func(t T) bool) []T {
resultSlice := []T{}
for _, v := range slice {
if fn(v) {
resultSlice = append(resultSlice, v)
}
}
return resultSlice
}
例えば、公開/非公開のステータスをもつDocumentがあって、今回は公開Documentを取り出したい、みたいな時使えそうかなと思ってます。
func main() {
doc1 := Document{Name: "doc1", Status: Draft}
doc2 := Document{Name: "doc2", Status: Draft}
doc3 := Document{Name: "doc3", Status: Published}
doc4 := Document{Name: "doc4", Status: Published}
docs := []Document{doc1, doc2, doc3, doc4}
fs := slices.FilterSlice(docs, func(t Document) bool {
return t.Status.IsPublished()
})
fmt.Printf("公開中のdocumentは%+v", fs) // 公開中のdocumentは[{Name:doc3 Status:1} {Name:doc4 Status:1}]
}
type Document struct {
Name string
Status DocStatus
}
type DocStatus int
const (
Draft DocStatus = iota
Published
)
func (ds DocStatus) IsPublished() bool {
return ds == Published
}
sliceの要素を削除する
mapはdelete関数あるのに、sliceにはないのか。。と思い作りました笑
func DeleteValue[T comparable](slice []T, target T) []T {
resultSlice := []T{}
for _, v := range slice {
if v != target {
resultSlice = append(resultSlice, v)
}
}
return resultSlice
}
使い方はシンプルでvalueを指定して、その要素を削除します。
func main() {
is := []int{1, 2, 3, 4}
ds := slices.DeleteValue(is, 3)
fmt.Println(ds) // [1, 2, 4]
}
おわり
最後まで読んでいただきありがとうございました!
1つでもこれ使えそう!と思ってもらえたら嬉しいです!
一応githubでもソースコード公開してます。
https://github.com/hiroaki-u/go-pkg