DEV Community

Shannon
Shannon

Posted on • Edited on

Using reflect.DeepEqual to compare data structures in Go

#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
}
Enter fullscreen mode Exit fullscreen mode

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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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")
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Top comments (1)

Collapse
 
bamnet profile image
Brian Michalski

As an alternative to reflect.DeepEquals, the github.com/google/go-cmp/cmp package lets you compare and sort slices pretty simply:

sort := func(x, y string) bool { return x > y }
if diff := cmp.Diff(want, got, cmpopts.SortSlices(sort)); diff != "" {
    t.Errorf("mismatch (-want +got):\n%s", diff)
}
Enter fullscreen mode Exit fullscreen mode