Come confrontare se due strutture, sezioni o mappe sono uguali?


131

Voglio verificare se due strutture, sezioni e mappe sono uguali.

Ma sto riscontrando problemi con il seguente codice. Vedi i miei commenti nelle righe pertinenti.

package main

import (
    "fmt"
    "reflect"
)

type T struct {
    X int
    Y string
    Z []int
    M map[string]int
}

func main() {
    t1 := T{
        X: 1,
        Y: "lei",
        Z: []int{1, 2, 3},
        M: map[string]int{
            "a": 1,
            "b": 2,
        },
    }

    t2 := T{
        X: 1,
        Y: "lei",
        Z: []int{1, 2, 3},
        M: map[string]int{
            "a": 1,
            "b": 2,
        },
    }

    fmt.Println(t2 == t1)
    //error - invalid operation: t2 == t1 (struct containing []int cannot be compared)

    fmt.Println(reflect.ValueOf(t2) == reflect.ValueOf(t1))
    //false
    fmt.Println(reflect.TypeOf(t2) == reflect.TypeOf(t1))
    //true

    //Update: slice or map
    a1 := []int{1, 2, 3, 4}
    a2 := []int{1, 2, 3, 4}

    fmt.Println(a1 == a2)
    //invalid operation: a1 == a2 (slice can only be compared to nil)

    m1 := map[string]int{
        "a": 1,
        "b": 2,
    }
    m2 := map[string]int{
        "a": 1,
        "b": 2,
    }
    fmt.Println(m1 == m2)
    // m1 == m2 (map can only be compared to nil)
}

http://play.golang.org/p/AZIzW2WunI


Inoltre, "operazione non valida: t2 == t1 (la struttura contenente la mappa [stringa] int non può essere confrontata)", ciò accade se la struttura non ha int [] nella sua definizione
Victor

Risposte:



69

reflect.DeepEqual viene spesso erroneamente utilizzato per confrontare due strutture simili, come nella tua domanda.

cmp.Equal è uno strumento migliore per confrontare le strutture.

Per capire perché la riflessione non è consigliata, diamo un'occhiata alla documentazione :

I valori di Struct sono profondamente uguali se i loro campi corrispondenti, sia esportati che non esportati, sono profondamente uguali.

....

numeri, bool, stringhe e canali - sono profondamente uguali se sono uguali usando l'operatore Go ==.

Se confrontiamo due time.Timevalori della stessa ora UTC, t1 == t2sarà falso se il loro fuso orario dei metadati è diverso.

go-cmpcerca il Equal()metodo e lo utilizza per confrontare correttamente i tempi.

Esempio:

m1 := map[string]int{
    "a": 1,
    "b": 2,
}
m2 := map[string]int{
    "a": 1,
    "b": 2,
}
fmt.Println(cmp.Equal(m1, m2)) // will result in true

9
Si, esattamente! Quando si scrivono i test, è molto importante usare go-cmpe non reflect.
Kevin Minehart,

Sfortunatamente né riflettono né cmp funzionano per confrontare una struttura con una fetta di puntatori a strutture. Vuole ancora che i puntatori siano gli stessi.
Violaman,

2
@GeneralLeeSpeaking non è vero. Dalla documentazione del cmp : "I puntatori sono uguali se anche i valori sottostanti a cui puntano sono uguali"
Ilia Choly,

Secondo la documentazione di cmp , l'utilizzo di cmp è consigliato solo quando si scrivono i test, perché potrebbe andare nel panico se gli oggetti non sono comparabili.
martin

17

Ecco come eseguirai la tua funzione http://play.golang.org/p/Qgw7XuLNhb

func compare(a, b T) bool {
  if &a == &b {
    return true
  }
  if a.X != b.X || a.Y != b.Y {
    return false
  }
  if len(a.Z) != len(b.Z) || len(a.M) != len(b.M) {
    return false
  }
  for i, v := range a.Z {
    if b.Z[i] != v {
      return false
    }
  }
  for k, v := range a.M {
    if b.M[k] != v {
      return false
    }
  }
  return true
}

3
Consiglierei di aggiungere if len(a.Z) != len(b.Z) || len(a.M) != len(b.M) { return false }, perché uno di loro potrebbe avere campi extra.
OneOfOne,

Tutte le informazioni strutturali sono note al momento della compilazione. È un peccato che il compilatore non possa fare questo sollevamento pesante in qualche modo.
Rick-777,

3
@ Rick-777 non c'è semplicemente alcun confronto definito per le sezioni. Ecco come volevano i progettisti del linguaggio. Non è così semplice definire come, per esempio, il confronto di interi semplici. Le sezioni sono uguali se contengono gli stessi elementi nello stesso ordine? Ma cosa succede se le loro capacità differiscono? Ecc.
Justinas,

1
if & a == & b {return true} Questo non valuterà mai true se i parametri da confrontare vengono passati per valore.
Sean,

4

Da luglio 2017 è possibile utilizzare cmp.Equalcon l' cmpopts.IgnoreFieldsopzione.

func TestPerson(t *testing.T) {
    type person struct {
        ID   int
        Name string
    }

    p1 := person{ID: 1, Name: "john doe"}
    p2 := person{ID: 2, Name: "john doe"}
    println(cmp.Equal(p1, p2))
    println(cmp.Equal(p1, p2, cmpopts.IgnoreFields(person{}, "ID")))

    // Prints:
    // false
    // true
}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.