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.SortFunc
と slices.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 })
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 })
記述量の少なさでは samber/lo のほうが優れていますが、サードパーティパッケージを依存に入れる必要があるのでそこをどうするかは個人の判断になります。
Goの標準パッケージにUniqやUniqFuncが増えることはないのか
標準パッケージに lo.Uniq
や lo.UniqBy
に対応する関数が増えたら便利そうに思いますが、入る余地はあるのでしょうか。
Goのリポジトリのissueを見ていたら以下のようなコメントがありました。
- https://github.com/golang/go/issues/41517#issuecomment-697803340
- https://github.com/golang/go/issues/41517#issuecomment-1607608922
特に Russ Cox による「重複排除とソートはわけたほうがよいと考えている」という意見が大きいのかなと思っていて、確かにUnixで sort
してから uniq
するという書き方と揃う。
ちなみにじゃあなんで Compact
って名前になったのかというと色々議論があった上、重複排除してくれる関数なのだとユーザが勘違いしそうということで最終的に Russ Cox の意見が通ったようです。 https://github.com/golang/go/issues/45955#issuecomment-880076961
また、Rob Pike さんもコアライブラリに Uniq
や UniqFunc
いらないのではとコメントしていた。(重複排除について言ってるのか、 Compact
も要らないと言ってるのかは読み取れなかったですが) https://github.com/golang/go/issues/45955#issuecomment-861173504
こういう感じなので、proposalを出したらチャンスはゼロではないかもしれないですが、「SortしてからCompactでいいのでは?」と言われる可能性が大きそうな印象を受けました。