434

How can I check if two slices are equal, given that the operators == and != are not an option?

package main

import "fmt"

func main() {
    s1 := []int{1, 2}
    s2 := []int{1, 2}
    fmt.Println(s1 == s2)
}

This does not compile with:

invalid operation: s1 == s2 (slice can only be compared to nil)

blackgreen
  • 34,072
  • 23
  • 111
  • 129
wei2912
  • 6,141
  • 2
  • 19
  • 20

13 Answers13

335

You should use reflect.DeepEqual()

DeepEqual is a recursive relaxation of Go's == operator.

DeepEqual reports whether x and y are “deeply equal,” defined as follows. Two values of identical type are deeply equal if one of the following cases applies. Values of distinct types are never deeply equal.

Array values are deeply equal when their corresponding elements are deeply equal.

Struct values are deeply equal if their corresponding fields, both exported and unexported, are deeply equal.

Func values are deeply equal if both are nil; otherwise they are not deeply equal.

Interface values are deeply equal if they hold deeply equal concrete values.

Map values are deeply equal if they are the same map object or if they have the same length and their corresponding keys (matched using Go equality) map to deeply equal values.

Pointer values are deeply equal if they are equal using Go's == operator or if they point to deeply equal values.

Slice values are deeply equal when all of the following are true: they are both nil or both non-nil, they have the same length, and either they point to the same initial entry of the same underlying array (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal. Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal.

Other values - numbers, bools, strings, and channels - are deeply equal if they are equal using Go's == operator.

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
Victor Deryagin
  • 11,895
  • 1
  • 29
  • 38
  • 23
    A very useful answer. Regardless of the general reflect package performance, it is very nice to have a prepackaged deep equality function for use in test cases where simplicity and correctness are paramount. – WeakPointer Mar 04 '15 at 01:56
  • 54
    I just ran a benchmark and reflect.DeepEqual is 150 times slower than a loop. Just FYI if anyone wants to use this method in production. – nikdeapen Apr 05 '17 at 03:08
  • 5
    It does not compare randomly orderded slices with same items :( – Hemant_Negi Jun 07 '17 at 07:01
  • 9
    @Hemant_Negi two slices aren't equal if they have a different order. If you want to compare equality of two slices while ignoring order then sort them and then check, or move the items from one slice into a map, and then check that each element on the other slice is in the map. ( additionally make sure they have same length ) – robbert229 Jun 13 '17 at 18:34
  • 11
    Rob Pike (in 2011) on reflection in Go, writing in the official Go blog: "It's a powerful tool that should be used with care and avoided unless strictly necessary" https://blog.golang.org/laws-of-reflection . I would not use reflection in production code just to compare slices. That's an easy function to write. But note that there's a potential flaw in the chosen answer to this question too, depending on what behavior you expect from it: it will find that slices that have been initialized but are still at len 0 and cap 0 do not match slices that have been declared but not initialized. – jrefior Jan 29 '18 at 16:50
  • I have objection to how this answer is phrased. It suggests using DeepEqual is the only valid option, while not mentioning the performance drawbacks mentioned in comments. I feel the author should be more clear about pros and cons of such solution. – Petr Aug 25 '21 at 22:04
218

You need to loop over each of the elements in the slice and test. Equality for slices is not defined. However, there is a bytes.Equal function if you are comparing values of type []byte.

func testEq(a, b []Type) bool {
    if len(a) != len(b) {
        return false
    }
    for i := range a {
        if a[i] != b[i] {
            return false
        }
    }
    return true
}
Inanc Gumus
  • 25,195
  • 9
  • 85
  • 101
Stephen Weinberg
  • 51,320
  • 14
  • 134
  • 113
  • 21
    Suggestion: `for i, v := range a { if v != b[i] { return false } }`. – zzzz Mar 09 '13 at 15:06
  • 41
    @zzzz Careful, this will fail on different lengths. – Filippo Valsorda Mar 30 '14 at 23:30
  • 2
    This does not work if the element type does not support ==. Also, IIUC, Go does not have anything like generics. This means that you must copy n' paste this function for each element type that you want to support. This is obviously something that should ship with the language. In fact, it does (albeit with the magic of reflect), and Victor provides the answer. The fact that this is chosen above that answer, and more highly voted is simply maddening... – allyourcode Feb 12 '15 at 07:54
  • 8
    Go as a language tends to recommend not using reflection unless absolutely necessary. Yes, it would need to be done for each type but it is generally not something you do often anyways. Also, reflect.DeepEqual may do something you don't expect such as saying two different pointers are equal because the values they point to are equal. – Stephen Weinberg Mar 25 '15 at 14:04
  • At the very least `testEq` needs the following prefix if you can not guarantee slices are not `nil` : `if a == nil && b == nil { return true; }; if a == nil || b == nil { return false; };`. But generally Victor Deryagin's answer should be preferred if environment permits. – Vlad Didenko May 04 '15 at 00:18
  • 3
    @FiloSottile Length is checked beforehand, the loop is only reached if lengths differ. – icza Aug 04 '15 at 14:53
  • 1
    @VladDidenko Nothing is required. The posted code properly works if any or all the input slices are `nil`s, both `len()` and `range` work on `nil` slices. Try it. – icza Aug 04 '15 at 14:56
  • @icza I was referring to zzzz's comment – Filippo Valsorda Aug 05 '15 at 12:40
  • @icza I did - and it was a headache bug to find. Like here: http://play.golang.org/p/tUrmFcxVzg . So it depends on your needed type of equality. – Vlad Didenko Aug 24 '15 at 22:42
  • 2
    @VladDidenko Now I see what you mean. Yes, you're right. Although in most of the cases I would take a `nil` slice be equal to a slice with 0 length, as which one my slice is can simply depend on how I create/initialize it. For example using short variable declaration it's common to write `s := []string{}` which will not be `nil`, but using `var` it will be `var s []string` which will initialize it to `nil`. Note that both are printed as an empty slice `[]`: [Go Playground](http://play.golang.org/p/MJYiSq1q39) – icza Aug 25 '15 at 06:10
  • @icza, exactly. To clarify a notch more, a nil slice may come from other places, not only initialization. For example, it is common for a slice-return value to have meaning, like used by the `regexp` package's `Find*` functions: http://golang.org/pkg/regexp/#Regexp.FindAll – Vlad Didenko Sep 08 '15 at 14:54
  • If you are able declare a specific *type* of slice that you want to use in a certain context, rather than try to test for equality for slices generally, e.g. `type intSlice []int`, then you can create an interface and implement an Equal method on this *type* which will allow comparing two slices of that type in the way you want. – Benjamin R Dec 03 '15 at 02:16
  • Aren't the top two if conditions (`if a == nil && b == nil` and `if a == nil || b == nil`) redudant? If both are nil their length would be equal, and if one of them is nil but not the other their length wouldn't match. This uses the fact that the length of a nil array is 0. – shanab Jan 12 '17 at 08:36
  • 2
    @Stefen-Weinberg Does this solution assume that arrays are sorted or I am missing something ? – l0n3r4n83r Feb 15 '17 at 17:40
  • 1
    This is a useful pattern. But note that it will return false when comparing a slice that has been declared but not initialized with a slice that has been initialized but still has len 0 and cap 0. In other words, two empty slices of the same type can be unequal under this definition. – jrefior Jan 29 '18 at 16:58
  • Second if seems to be not needed. Also I would assume that having both arguments nil is unlikely. I'm using this func isEqual(a, b []Type) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return a == nil && b == nil } – Kris Kwiatkowski Apr 26 '18 at 10:15
  • None of these if statements are redundant although you are free to remove both nil check if statements if you don't care about nil vs zero length slices. However, after some thought, they could be combined into a single if statement, I will make that change. The end result requires a comment to explain it so I am not sure it is better. As for if this assumes arrays are sorted: I consider slices equal only if they have the same values in the same order. Not that they are sorted per say, but that they have the same order. – Stephen Weinberg Aug 05 '18 at 03:03
99

This is just example using reflect.DeepEqual() that is given in @VictorDeryagin's answer.

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))

}

Result:

true
false

Try it in Go Playground

‌‌R‌‌‌.
  • 2,818
  • 26
  • 37
Akavall
  • 82,592
  • 51
  • 207
  • 251
57

If you have two []byte, compare them using bytes.Equal. The Golang documentation says:

Equal returns a boolean reporting whether a and b are the same length and contain the same bytes. A nil argument is equivalent to an empty slice.

Usage:

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))
}

This will print

true
false
KeksArmee
  • 1,349
  • 14
  • 21
17

And for now, here is https://github.com/google/go-cmp which

is intended to be a more powerful and safer alternative to reflect.DeepEqual for comparing whether two values are semantically equal.

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
}
lk_vc
  • 1,136
  • 20
  • 26
14

You cannot use == or != with slices but if you can use them with the elements then Go 1.18 has a new function to easily compare two slices, slices.Equal:

Equal reports whether two slices are equal: the same length and all elements equal. If the lengths are different, Equal returns false. Otherwise, the elements are compared in increasing index order, and the comparison stops at the first unequal pair. Floating point NaNs are not considered equal.

The slices package import path is golang.org/x/exp/slices. Code inside exp package is experimental, not yet stable. It will be moved into the standard library in Go 1.19 eventually.

Nevertheless you can use it as soon as Go 1.18 (playground)

    sliceA := []int{1, 2}
    sliceB := []int{1, 2}
    equal := slices.Equal(sliceA, sliceB)
    fmt.Println(equal) // true

    type data struct {
        num   float64
        label string
    }

    sliceC := []data{{10.99, "toy"}, {500.49, "phone"}}
    sliceD := []data{{10.99, "toy"}, {200.0, "phone"}}
    equal = slices.Equal(sliceC, sliceD)
    fmt.Println(equal) // true

If the elements of the slice don't allow == and !=, you can use slices.EqualFunc and define whatever comparator function makes sense for the element type.

blackgreen
  • 34,072
  • 23
  • 111
  • 129
7

In case that you are interested in writing a test, then github.com/stretchr/testify/assert is your friend.

Import the library at the very beginning of the file:

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

Then inside the test you do:


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

The error prompted will be:

                Diff:
                --- Expected
                +++ Actual
                @@ -1,4 +1,4 @@
                 ([]int) (len=2) {
                + (int) 1,
                  (int) 2,
                - (int) 2,
                  (int) 1,
Test:           TestEquality_SomeSlice
Mark Harrison
  • 297,451
  • 125
  • 333
  • 465
Gabriel Furstenheim
  • 2,969
  • 30
  • 27
  • `assert.Equal` internally uses `reflect.DeepEqual` which might make your tests to run slower and eventually your pipeline. – Deepak Sah Nov 21 '19 at 07:07
  • 2
    @DeepakSah Do you have benchmarks for the performance difference? In my experience performance bottleneck in the tests is not in the assert equal, and you get great quality messages which has a boost in productivity – Gabriel Furstenheim Nov 22 '19 at 08:07
  • 5
    You could use `assert.ElementsMatch(t, a, b)` to ignore the elements order. – marcelosalloum Sep 29 '20 at 21:31
2

Thought of a neat trick and figured I'd share.

If what you are interested in knowing is whether two slices are identical (i.e. they alias the same region of data) instead of merely equal (the value at each index of one slice equals the value in the same index of the other) then you can efficiently compare them in the following way:

foo := []int{1,3,5,7,9,11,13,15,17,19}

// these two slices are exactly identical
subslice1 := foo[3:][:4]
subslice2 := foo[:7][3:]

slicesEqual := &subslice1[0]  == &subslice2[0]   && 
               len(subslice1) == len(subslice2)

There are some caveats to this sort of comparison, in particular that you cannot compare empty slices in this way, and that the capacity of the slices isn't compared, so this "identicality" property is only really useful when reading from a slice or reslicing a strictly narrower subslice, as any attempt to grow the slice will be affected by the slices' capacity. Still, it's very useful to be able to efficiently declare, "these two huge blocks of memory are in fact the same block, yes or no."

Wug
  • 12,956
  • 4
  • 34
  • 54
  • you have trailing parenthesis breaking the code syntax –  Aug 12 '21 at 18:02
  • someone might want to run `fmt.Printf("%p %p\n", &subslice1[0], &subslice2[0])` to see that both shares the same memory address. and that it works fine as long as he compares the same index against both slices. ie `fmt.Printf("%p %p\n", &subslice1[1], &subslice2[1])` etc –  Aug 12 '21 at 18:21
  • 1
    They will. Reslicing doesn't reallocate, it aliases the storage in use by the original slice, and slices are also always contiguous so there's no way to end up with a slice where this will be true with some indices but not others. – Wug Aug 31 '21 at 22:20
1

To have a complete set of answers: here is a solution with generics.

func IsEqual[A comparable](a, b []A) bool {
    // Can't be equal if length differs
    if len(a) != len(b) {
        return false
    }

    // Empty arrays trivially equal
    if len(a) == 0 {
        return true
    }

    // Two pointers going towards each other at every iteration
    left := 0
    right := len(a) - 1

    for left <= right {
        if a[left] != b[left] || a[right] != b[right] {
            return false
        }

        left++
        right--
    }

    return true
}

Code uses strategy of "two pointers" which brings runtime complexity of n / 2, which is still O(n), however, twice as less steps than a linear check one-by-one.

Update: Fixed equality check bug as per @doublethink13

Community
  • 1
  • 1
pvlbzn
  • 138
  • 7
  • There is a subtle bug in your code. This fails if you are comparing slices of length 1 and fails if you are comparing odd length slices where only the middle element is different. `for left < right` needs to change to `for left <= right` – doublethink13 Jun 10 '23 at 14:56
  • 1
    You are absolutely right @doublethink13 , edited! – pvlbzn Jun 11 '23 at 20:33
0

There is function assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) for checking slices.

Thanks Aaron for comment, It is not obvious so I add highlight this assert requires lib "github.com/stretchr/testify/assert"

mapcuk
  • 800
  • 1
  • 7
  • 21
0

Golang has introduced a package Slices with various functions useful with slices of any type. And we can use Equal function which reports whether two slices are equal.

https://cs.opensource.google/go/x/exp/+/06a737ee:slices/slices.go;l=22

// Equal reports whether two slices are equal: the same length and all
// elements equal. If the lengths are different, Equal returns false.
// Otherwise, the elements are compared in increasing index order, and the
// comparison stops at the first unequal pair.
// Floating point NaNs are not considered equal.
func Equal[E comparable](s1, s2 []E) bool {
    if len(s1) != len(s2) {
        return false
    }
    for i := range s1 {
        if s1[i] != s2[i] {
            return false
        }
    }
    return true
}

code

package main

import (
    "fmt"

    "golang.org/x/exp/slices"
)

func main() {
    s1 := []int{1, 2}
    s2 := []int{1, 2}

    equal := slices.Equal(s1, s2)
    fmt.Println("Is Equal ? ", equal)
}

PRATHEESH PC
  • 1,461
  • 1
  • 3
  • 15
0

As of Go 1.21, you can use a generic function slices.Equal() from the standard library:

package main

import (
    "fmt"
    "slices"
)

func main() {
    s1 := []int{1, 4, 1, 4, 2, 1, 3, 5, 6, 2}
    s2 := []int{1, 4, 1, 4, 2, 1, 3, 5, 6, 2}

    fmt.Println(slices.Equal(s1, s2))

    s3 := []string{"foo", "bar"}
    s4 := []string{"foo", "baz"}

    fmt.Println(slices.Equal(s3, s4))
}

Playground: https://go.dev/play/p/_WwU0BSwN2P

Z. Kosanovic
  • 727
  • 4
  • 10
-1

Go language provides inbuilt support implementation for this. The reflect.DeepEqual() Function in Golang is used to check whether x and y are “deeply equal” or not. To access this function, one needs to imports the reflect package in the program.

Syntax: func DeepEqual(x, y interface{}) bool

Parameters: This function takes two parameters with value of any type, i.e. x, y.

Return Value: This function returns the boolean value.

For example: If you want to check whether map_1 and map_2 are equal or not

result := reflect.DeepEqual(map_1, map_2)

result will be true if map_1 and map_2 are equal, and result will false if map_1 and map_2 are not equal.