Using reflect.DeepEqual to compare data structures in Go

Intro

When I started using Go, I would compare two slices by writing a for loop and iterating over both to make sure they were equal. After seeing some sample code using the reflect package, I realized this is totally unnecessary. We can compare slices, maps, structs (anything!) simply by passing them into reflect.DeepEqual(x,y), which reports whether two variables are "deeply equal."

Slice/Array Example

Let's take this function that reverses a slice:

func ReverseSlice(s []string) []string {
    reversedSlice := make([]string, len(s))

    for i, j := len(s), 0; i > 0; i, j = i-1, j+1 {
        reversedSlice[j] = s[i-1]
    }

    return reversedSlice
}

Now, I have written a test that shows the usage or comparing two slices:

func TestReverseSlice(t *testing.T) {
    got := ReverseSlice([]string{
        "person1",
        "person2",
        "person3",
        "person4",
    })

    want := []string{
        "person4",
        "person3",
        "person2",
        "person1",
    }

    if !reflect.DeepEqual(got, want) {
        t.Errorf("got %q slice but expected %q", got, want)
    }
}

So, pretty straightforward! However, it's really important to note that the two arrays must be in the same order. They cannot have the same values but be in a different order. The function comments specifically state: Array values are deeply equal when their corresponding elements are deeply equal.

Map Example

Maps are a little different! By default, they are not going to have a specific order. For instance, a map[string]string is going to be constructed with a potentially random order.

So how are they compared with DeepEqual? They will both need to either be nil, or not nil, have the same length, and the same corresponding key/values.

In this case, I'm going to just use main() to showcase a few of these points with print statements:

func main() {
    m1, m2 := make(map[string]string), make(map[string]string)

    m1["person1"] = "person1"
    m1["person2"] = "person2"

    m2["person2"] = "person2"
    m2["person1"] = "person1"

    fmt.Printf("type=%T -- addr=%p -- len=%d\n", m1, &m1, len(m1))
    fmt.Printf("type=%T -- addr=%p -- len=%d\n", m2, &m2, len(m2))

    if reflect.DeepEqual(m1, m2) {
        fmt.Println("they are equal")
    } else {
        fmt.Println("they are NOT equal")
    }
}

Specifically, we're looking to compare the type, address, and length of the map. We'll need the type and length to be the same, but the addresses should be different (but not nil).

Once running, you should see output similar to this:

type=map[string]string -- addr=0xc0000b0018 -- len=2
type=map[string]string -- addr=0xc0000b0020 -- len=2
they are equal

I'd also encourage reading the full documentation for this function in the Go docs :)

10