emahiro/b.log

Drastically Repeat Yourself !!!!

Goのsliceで重複を削除する

重複削除処理を実装する

ruby でいうところの uniq メソッドみたいなものが golang の slice にもないのかと思って調べてみたけどないらしいので、重複のある slice に対して独自に処理を実装しなければ行けない。

go は非常にシンプルでLL言語をずっと書いていたエンジニアとしては覚えることが比較的少なく、静的型付けされていて安全で、高速という点で有用なツールだと思うけど、シンプルすぎる反面、LL言語ならデフォルトのAPIやメソッドとして提供されていそうな処理が実装されてなかったりすることが多い。。。

LL言語の処理をブラックボックス化してしまうことに比べれば、いいことのように感じる一方で、これくらい用意しておいてよ~的な感想もしばしば(笑)

というわけで go における slice の重複削除の実装方法を調べてみた。

方法

  1. mapを使う
  2. mapと空のstructを使う

map を使う方法

make(map[string]int) or make(map[string]bool) のようにkeyに配列の要素をおいて値を適当にintやboolをぶち込みます。

arr := [string]{"a", "b", "c", "a"}
m := make(map[string]bool)
uniq := [] string{}

for _, ele := range arr {
    if !m[ele] {
        m[ele] = true
        uniq = append(uniq, ele)
    }
}

fmt.Printf("%v", uniq) // ["a", "b", "c"]

どういうことかというと、重複キーがあるので、同様のキーを持つmapの場合は新しく値を上書きしない。
ここの処理で言うところの m["a"] = true は一度目はこれが呼ばれるけど、二度目はすでに true なので if句の中に入ってず、resultに a が二度入ることがない。
こんな感じでmapにboolをいれることで重複を削除する方法はgoでは一般的です。

空のstructとの合わせ技

だいたいのユースケースにおいてintかboolをぶち込む方法で事足りる前提で付け足しですが、空のstructを使っても同じことができます。
実際に出会う機会があるかはわかりませんが、メモリ的にきついめの状況があるかもしれず、intやboolでいちいちメモリ空間とってられん、的な状況があるかもしれません。(ないかもしれません)

arr := [string]{"a", "b", "c", "a"}
m := make(map[string]struct{}) // 空のstructを使う

for _, ele := range arr {
    m[ele] = struct{}{} // m["a"] = struct{}{} が二度目は同じものとみなされて重複が消える。
}

uniq := [] string{}
for i := range m {
    uniq := append(uniq, i)
}

fmt.Printf("%v", uniq) // ["a", "b", "c"]

loopを二度使わなければなりませんが、intやboolようにメモリを確保しなくていいので省メモリで実装できます。 struct{}{} はgoで出て来るお作法みないなもので、valueに意味はないよっていうこと表します。