emahiro/b.log

Drastically Repeat Yourself !!!!

slice 操作や検証のあれこれ

ある slice の操作や検証をするときにスッキリ書く方法を教えてもらったので備忘録。

※ 随時更新(忘れて新しくエントリ書くこともあるかも)

重複する要素を持つ slice を弾きたいとき

func duplicate(arr []string) bool {
    base := map[string]int{}

    for _, k := range arr {
        if i := base[k]; i != 0 {
            return true
        }
        base[k]++
    }
    return false
}

base のような slice において []string{"a", "a", "b"} みたいな重複する要素を持つ配列を弾きたい時に使います。

ref: https://play.golang.org/p/bW5YFckAZmB

base の部分も引数に渡してもいいかもしれませんが、ユースケースごとに作ってもいいと思います。
int を使用したのは重複チェックするなら重複した要素をカウントするのがいいかなと思ったためですね。

ただ、場合によっては int より効率いいもの使いたい!っていうケースもあると思うのでそしたら bool を使うのがいいと思います。
typeごとのデータサイズは https://golang.org/src/go/types/sizes.gobasicSizes を見るとわかります。

func deplicate(arr []string) bool {
    base := map[string]bool{}

    for _, k := range arr {
        if ok := base[k]; ok {
            return true
        }
        base[k] = true
    }
    return false
}

ref: https://play.golang.org/p/m_g0u9TCWTX

今思うとこっちの方がスッキリしてますね。

意図しない要素をもつ slice を弾きたいとき

func hasInvalidElement(arr []string) bool {
    base := map[string]int{
        "a": 0,
        "b": 0,
        "c": 0,
    }

    for _, k := range arr {
        if _, ok := base[k]; !ok {
            return true
        }
    }
    return false
}

base にない要素をもつ slice を弾きます。
ref: https://play.golang.org/p/Rnc6SFbx1il

このとき要素の重複は許可する場合 e.g. []string{"a", "a", "b", "c"} には個数のカウントはしません。

そしてこれももっとスッキリ書くなら bool にすると良さそうですね、ってことで書き直してみます。

func hasInvalidElement(arr []string) bool {
    base := map[string]bool{
        "a": true,
        "b": true,
        "c": true,
    }

    for _, k := range arr {
        if ok := base[k]; !ok {
            return true
        }
    }
    return false
}

ref: https://play.golang.org/p/SRGe4ZqeTrr

さらにメモリ効率を良くしたい!と言ったときは bool ではなくて struct{} を使用してもいいかもしれません。

func hasInvalidElement(arr []string) bool {
    base := map[string]struct{}{
        "a": struct{}{},
        "b": struct{}{},
        "c": struct{}{},
    }

    for _, k := range arr {
        if _, ok := base[k]; !ok {
            return true
        }
    }
    return false
}

ref: https://play.golang.org/p/Wrpuc0JTupn

組み合わせ

これらを組み合わせて例えば 要素はお互いがユニークかつ決まった要素が入ってるsliceを検証する と言ったslice の検証要件があったときに以下のようなバリデーションを考えてみます。

func isValid(arr []string) bool {
    base := map[string]bool{
        "a": false,
        "b": false,
        "c": false,
    }

    for _, k := range arr {
        if got, ok := base[k]; !ok || got {
            return false
        }
        base[k] = true
    }
    return true
}

map[string]bool を使って上記のように書くことが可能です。
ref: https://play.golang.org/p/r8qBfhhhq6m

過去に書いた slice 操作系のエントリ

slice の操作は色々考えてみると結構面白いです。 また何か追加するかもしれません。

過去書いた slice 周りのエントリは以下です。

ema-hiro.hatenablog.com

ema-hiro.hatenablog.com