stefafafan の fa は3つです

"すてにゃん" こと id:stefafafan のブログです

Go言語でsliceの重複排除 (slicesやsamber/loパッケージを使う場合)

Go言語でsliceの重複排除について書きます。Go 1.21前提です。

slices パッケージを使っての重複排除

Go 1.21から slices パッケージが増えました。ここに生えている関数を利用して重複削除のコードが書けます。
pkg.go.dev

例えば int の slice の重複排除は以下のように書けます。

integers := []int{1, 2, 2, 1}
slices.Sort(integers) // [1 1 2 2]
uniqValues := slices.Compact(integers) // [1 2]

slices.Compact は連続する値を1つにまとめる関数なので、重複排除したい場合は slices.Sort で先にソートする必要があります。

User という struct の slice を id で重複排除したい場合はどうすればいいかというと、比較関数を自分で渡せる slices.SortFuncslices.CompactFunc も用意されているので、以下のように書けます。

slices.SortFunc(users, func(a, b User) int {
	return cmp.Compare(a.id, b.id)
})
uniqUsers := slices.CompactFunc(users, func(a, b User) bool {
	return a.id == b.id
})

Go Playground での例

samber/lo パッケージを使っての重複排除

samber/lo というLodash風のサードパーティなライブラリがあり、これを使うとより短い記述で同じことが実現できます。
github.com

int の slice の重複排除は1行で実現できます。

uniqValues := lo.Uniq([]int{1, 2, 2, 1}) // [1 2]

User の slice の重複排除は UniqBy という便利な関数が用意されているので以下の3行でシンプルに書けます。

uniqUsers := lo.UniqBy(users, func(u User) int {
	return u.id
})

Go Playgroundでの例

記述量の少なさでは samber/lo のほうが優れていますが、サードパーティパッケージを依存に入れる必要があるのでそこをどうするかは個人の判断になります。

Goの標準パッケージにUniqやUniqFuncが増えることはないのか

標準パッケージに lo.Uniqlo.UniqBy に対応する関数が増えたら便利そうに思いますが、入る余地はあるのでしょうか。

Goのリポジトリのissueを見ていたら以下のようなコメントがありました。

特に Russ Cox による「重複排除とソートはわけたほうがよいと考えている」という意見が大きいのかなと思っていて、確かにUnixsort してから uniq するという書き方と揃う。

ちなみにじゃあなんで Compact って名前になったのかというと色々議論があった上、重複排除してくれる関数なのだとユーザが勘違いしそうということで最終的に Russ Cox の意見が通ったようです。 https://github.com/golang/go/issues/45955#issuecomment-880076961

また、Rob Pike さんもコアライブラリに UniqUniqFunc いらないのではとコメントしていた。(重複排除について言ってるのか、 Compact も要らないと言ってるのかは読み取れなかったですが) https://github.com/golang/go/issues/45955#issuecomment-861173504

こういう感じなので、proposalを出したらチャンスはゼロではないかもしれないですが、「SortしてからCompactでいいのでは?」と言われる可能性が大きそうな印象を受けました。