2つのスライスが等しいかどうかの確認


274

2つのスライスが等しいかどうかを確認するにはどうすればよいですか?


111
質問は実際には単純なタスクに関するものですが、IMOは非常に具体的な答えを持つ実際の質問です。Goのタグが付けられた質問に積極的に参加したことを思い出せない人が、私の知る限り、「本当の質問ではない」と締めくくられた方法は、私を超えています。具体的には、質問はあいまいではなく、完全で、単一の(単純ではあるが)問題に絞り込まれ、修辞ではなく、現在の形式で正確かつ正確に回答できます。==オペレータはそうしかも、この質問にも正当な一つであり、唯一のいくつかのタイプのために行くに定義されています。
zzzz 2013年

4
それでも、それは詳細な理由で述べられているものではありません(「現在の形式では合理的に回答できません」)。
リッチチャーチャー2013年

9
ははは、これが「本当の質問ではない」という理由で閉じられたなんて信じられません。1)何が尋ねられているかを言うことは難しくありません。2)質問があいまい/不完全/広範囲/不合理ではない。これはかなりの乱用です!
weberc2 2013年

5
現在のところ、「反対票」ボタン(「この質問は努力を示すものではなく、よく聞かれていません」)を「閉じる」ボタン(「次の理由により、回答できないと思います。」 。」)。クローズ投票が無料であるためかもしれません。
コス

3
Goで開発されてに対抗しslice can only be compared to nil、スライスの等価性をチェックする慣用的なgolangの方法があるかどうか疑問に思っていました...等価演算子が言語によって定義されていない場合、最も効率的な方法を尋ねるのが妥当だと思いますそれを達成するために。質問を閉じる必要はありませんでした
abgordon

回答:


157

スライスの各要素をループしてテストする必要があります。スライスの等価性は定義されていません。ただし、bytes.Equaltypeの値を比較する場合は関数があります[]byte

func testEq(a, b []Type) bool {

    // If one is nil, the other must also be nil.
    if (a == nil) != (b == nil) { 
        return false; 
    }

    if len(a) != len(b) {
        return false
    }

    for i := range a {
        if a[i] != b[i] {
            return false
        }
    }

    return true
}

15
提案:for i, v := range a { if v != b[i] { return false } }
zzzz

19
@zzzz注意、これはさまざまな長さで失敗します。
FiloSottile 14年

2
要素タイプが==をサポートしていない場合、これは機能しません。また、IIUC、Goにはジェネリックのようなものはありません。つまり、サポートする要素タイプごとにこの関数をコピーして貼り付ける必要があります。これは明らかに言語に同梱されるべきものです。実際、それは(リフレクトの魔法ではありますが)あり、ビクターが答えを出します。これがその回答より上で選択され、投票数が多いという事実は単純に馬鹿げています...
allyourcode

5
言語としてのGoは、どうしても必要な場合を除き、リフレクションを使用しないことを推奨する傾向があります。はい、それはタイプごとに実行する必要がありますが、それは通常、とにかく頻繁に行うことではありません。また、reflect.DeepEqualは、2つの異なるポインターが指す値が等しいため、それらが等しいと言うなど、予期しない動作をする場合があります。
Stephen Weinberg

2
@FiloSottile長さは事前にチェックされ、長さが異なる場合にのみループに到達します。
icza

259

あなたは使うべきです reflect.DeepEqual()

DeepEqualは、Goの==演算子の再帰的な緩和です。

DeepEqualは、xとyが「深く等しい」かどうかを報告します。以下のように定義されています。同じタイプの2つの値は、次のいずれかの場合が当てはまる場合、完全に等しくなります。特殊タイプの値が深く等しくなることはありません。

配列の値は、対応する要素が深く等しい場合に深く等しくなります。

エクスポートされる値とエクスポートされない値の両方に対応するフィールドが深く等しい場合、構造体の値は深く等しくなります。

両方がnilの場合、Func値は深く等しくなります。そうでなければ、それらは深くは等しくありません。

インターフェースの値は、具体的な値が深く等しい場合、深く等しくなります。

マップ値は、それらが同じマップオブジェクトである場合、またはそれらが同じ長さで、対応するキー(Go等式を使用して一致する)が深く等しい値にマップされている場合、深く等しくなります。

ポインターの値は、Goの==演算子を使用して等しい場合、または深く等しい値を指している場合、深く等しくなります。

スライス値は、次のすべてが真である場合に深く等しくなります。両方ともnilまたは両方とも非nilであり、それらは同じ長さであり、同じ基本配列の同じ初期エントリを指している(つまり、&x [0 ] ==&y [0])または対応する要素(長さまで)が深く等しい。nil以外の空のスライスとnilスライス(たとえば、[] byte {}と[] byte(nil))は完全には等しくないことに注意してください。

他の値-数値、ブール値、文字列、チャネル-は、Goの==演算子を使用して等しい場合、完全に等しくなります。


13
非常に役立つ答えです。一般的なリフレクトパッケージのパフォーマンスに関係なく、単純さと正確さが最重要であるテストケースで使用するために、事前にパッケージ化されたディープイコライゼーション関数があると非常に便利です。
WeakPointer 2015年

15
ベンチマークを実行して反映したところです.DeepEqualはループより150倍遅くなります。この方法を本番環境で使用したい場合は注意してください。
nikdeapen 2017

2
ランダムに並べられたスライスを同じアイテムと比較しません:(
Hemant_Negi

5
@Hemant_Negi 2つのスライスは、順序が異なる場合は等しくありません。順序を無視して2つのスライスの同等性を比較する場合は、それらを並べ替えてから確認するか、アイテムを1つのスライスからマップに移動して、他のスライスの各要素がマップにあることを確認します。(さらに、それらの長さが同じであることを確認してください)
robbert229

3
Goのリフレクションに関するRob Pike(2011年)は、Goの公式ブログに書いています:「これは強力なツールであり、厳密に必要な場合を除き、慎重に使用し、回避する必要があります」blog.golang.org/laws-of-reflection。スライスを比較するためだけに、プロダクションコードでリフレクションを使用しません。これは簡単に書くことができる関数です。ただし、予想される動作によっては、この質問に対する選択した回答にも潜在的な欠陥があることに注意してください。初期化されているが、len 0およびcap 0にあるスライスは、以前に作成されたスライスと一致しないことがわかります。宣言されていますが、初期化されていません。
jrefior

44

これは、@ VictorDeryaginの回答で示されているReflect.DeepEqual()を使用した単なる例です。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    a := []int {4,5,6}
    b := []int {4,5,6}
    c := []int {4,5,6,7}

    fmt.Println(reflect.DeepEqual(a, b))
    fmt.Println(reflect.DeepEqual(a, c))

}

結果:

true
false

Go Playgroundで試す


23

2つある場合は[]bytebytes.Equalを使用して比較します。Golangのドキュメントによれば、

Equalは、aとbが同じ長さで、同じバイトを含むかどうかを報告するブール値を返します。nil引数は空のスライスと同等です。

使用法:

package main

import (
    "fmt"
    "bytes"
)

func main() {
    a := []byte {1,2,3}
    b := []byte {1,2,3}
    c := []byte {1,2,2}

    fmt.Println(bytes.Equal(a, b))
    fmt.Println(bytes.Equal(a, c))
}

これは印刷されます

true
false

なぜこれがトップではないのか
lurf jurv

3

そして今のところ、これはhttps://github.com/google/go-cmpです

reflect.DeepEqual2つの値が意味的に等しいかどうかを比較するためのより強力で安全な代替手段となることを目的としています。

package main

import (
    "fmt"

    "github.com/google/go-cmp/cmp"
)

func main() {
    a := []byte{1, 2, 3}
    b := []byte{1, 2, 3}

    fmt.Println(cmp.Equal(a, b)) // true
}

1

テストを書くことに興味がある場合は、 github.com/stretchr/testify/assertは、あなたの友達です。

ファイルの最初にライブラリをインポートします。

import (
    "github.com/stretchr/testify/assert"
)

次に、あなたが行うテストの中で:


func TestEquality_SomeSlice (t * testing.T) {
    a := []int{1, 2}
    b := []int{2, 1}
    assert.Equal(t, a, b)
}

要求されるエラーは次のとおりです。

                Diff:
                --- Expected
                +++ Actual
                @@ -1,4 +1,4 @@
                 ([]int) (len=2) {
                + (int) 1,
                  (int) 2,
                - (int) 2,
                  (int) 1,
Test:           TestEquality_SomeSlice

assert.Equal内部で使用しreflect.DeepEqualているため、テストの実行が遅くなり、最終的にはパイプラインが実行される可能性があります。
Deepak Sah

@DeepakSahパフォーマンスの違いのベンチマークはありますか?私の経験では、テストのパフォーマンスボトルネックは同等ではなく、生産性を向上させる優れた品質のメッセージが得られます
Gabriel Furstenheim '22
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.