结构的唯一哈希

I would like to generate a unique hash from an array of structs. The order might be different but the values are the same.

Example:

type MyStruct struct {
   ID string
   Parameters map[string]interface{}
}

arr:= []MyStruct{MyStruct{ID: "test", Parameters: map[string]interface{}{"key": true, "key2": "value"} }, MyStruct{ID: "test2", Parameters: map[string]interface{}{"key2": "value", "key": true} }}
//The order is different even inside the Parameters fields

arr:= []MyStruct{MyStruct{ID: "test2", Parameters: map[string]interface{}{"key2": "value", "key": true} },MyStruct{ID: "test", Parameters: map[string]interface{}{"key": true, "key2": "value"} }}

In both cases the hash should be the same since the values inside the structs are the same.

EDIT: Basically the idea is to generate a unique hash for caching! I want to combine the individual hash of each struct!

You can do this by serializing the array to an array of bytes, then computing the md5 sum of the byte array. Since the md5 sum is the hashed sum of the bytes, the order will not matter.

To serialize each element of the array, you can use json.Marshal, which will work for any type of struct.

The hash function will look something like this :

func Hash(arr []SomeStruct) [16]byte {
    arrBytes := []byte{}
    for _, item := range arr {
        jsonBytes, _ := json.Marshal(item)
        arrBytes = append(arrBytes, jsonBytes...)
    }
    return md5.Sum(arrBytes)
}

You can run the working sample program here

func main() {
    arr1 := []SomeStruct{
        {
            param1: "abc",
            param2: 3,
        },
        {
            param1: "def",
            param2: 5,
        }, {
            param1: "deg",
            param2: 0,
        },
    }
    arr2 := []SomeStruct{
    {
            param1: "deg",
            param2: 0,
        },
        {
            param1: "def",
            param2: 5,
        },
        {
            param1: "abc",
            param2: 3,
        },
    }

    fmt.Printf("Hash1: %x
", Hash(arr1))
    fmt.Printf("Hash2: %x
", Hash(arr2))

}

func Hash(arr []SomeStruct) [16]byte {
    arrBytes := []byte{}
    for _, item := range arr {
        jsonBytes, _ := json.Marshal(item)
        arrBytes = append(arrBytes, jsonBytes...)
    }
    return md5.Sum(arrBytes)
}

Which will output:

Hash1: d065fee603fdcf75115204ec65310e1c
Hash2: d065fee603fdcf75115204ec65310e1c

Feng has a point, the accepted answer doesn't work not only because there are no exported fields in the struct, but also the fact that the way MD5 hashes does have order significance, see RFC 1321 3.4.

Not sure on efficacy, but I've got something working by passing in the slice as bytes using encoding/gob and bytes representing a hash to use in Compare.

Playground

package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
)

type S struct {
    K1 string
    K2 int
}

func main() {
    sa := []S{{ K1: "foo", K2: 1}, {K1: "bar", K2: 2}, {K1: "baz", K2: 3,}}
    sb := []S{{ K1: "baz", K2: 3}, {K1: "bar", K2: 2}, {K1: "foo", K2: 1,}}
    sc := []S{}

    a := Hash(sa)
    b := Hash(sb)
    c := Hash(sc)

    fmt.Println(Compare(a, b))
    fmt.Println(Compare(a, c))
}

func Compare(a, b []byte) bool {
    a = append(a, b...)
    c := 0
    for _, x := range a {
        c ^= int(x)
    }
    return c == 0
}

func Hash(s []S) []byte {
    var b bytes.Buffer
    gob.NewEncoder(&b).Encode(s)
    return b.Bytes()
}

Maybe https://github.com/mitchellh/hashstructure could help you. It consistently hashes struct but can be used with slices and arrays.