Me gustaría generar un hash único a partir de una matriz de estructuras. El orden puede ser diferente pero los valores son los mismos.

Ejemplo:

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

En ambos casos, el hash debe ser el mismo ya que los valores dentro de las estructuras son los mismos.

EDITAR: ¡Básicamente, la idea es generar un hash único para el almacenamiento en caché! ¡Quiero combinar el hash individual de cada estructura!

1
Marco Talento 14 ene. 2018 a las 22:38

3 respuestas

La mejor respuesta

Puede hacer esto serializando la matriz a una matriz de bytes, luego calculando la suma md5 de la matriz de bytes. Dado que la suma md5 es la suma hash de los bytes, el orden no importará.

Para serializar cada elemento de la matriz, puede usar json.Marshal, que funcionará para cualquier tipo de struct.

La función hash se verá más o menos así:

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

Puede ejecutar el programa de ejemplo de trabajo aquí

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\n", Hash(arr1))
    fmt.Printf("Hash2: %x\n", 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)
}

Lo que dará salida:

Hash1: d065fee603fdcf75115204ec65310e1c
Hash2: d065fee603fdcf75115204ec65310e1c
-1
Soham Kamani 15 ene. 2018 a las 11:55

Quizás https://github.com/mitchellh/hashstructure podría ayudarlo. Constantemente hashes estructura pero se puede utilizar con sectores y matrices.

1
M C 2 sep. 2019 a las 12:13

Feng tiene un punto, la respuesta aceptada no funciona no solo porque no hay campos exportados en la estructura, sino también por el hecho de que el hash MD5 tiene importancia para el orden, vea RFC 1321 3.4.

No estoy seguro de la eficacia, pero tengo algo que funciona al pasar el segmento como bytes usando encoding/gob y bytes que representa un hash para usar en Compare.

Patio de recreo

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()
}
5
woat 5 sep. 2018 a las 22:31