go

Using reflect.DeepEqual to compare data structures in Go

lucassha

Shannon

Posted on July 14, 2021

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

πŸ’– πŸ’ͺ πŸ™… 🚩
lucassha
Shannon

Posted on July 14, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024

How to Use KitOps with MLflow
beginners How to Use KitOps with MLflow

November 29, 2024