Wie kann man vergleichen, ob zwei Strukturen, Slices oder Maps gleich sind?


131

Ich möchte überprüfen, ob zwei Strukturen, Slices und Maps gleich sind.

Aber ich habe Probleme mit dem folgenden Code. Siehe meine Kommentare in den entsprechenden Zeilen.

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


COnsider auch 'ungültige Operation: t2 == t1 (Struktur mit map [string] int kann nicht verglichen werden)', dies passiert, wenn die Struktur kein int [] in seiner Definition hat
Victor

Antworten:



69

reflect.DeepEqual wird oft fälschlicherweise verwendet, um zwei ähnliche Strukturen zu vergleichen, wie in Ihrer Frage.

cmp.Equal ist ein besseres Werkzeug zum Vergleichen von Strukturen.

Um zu sehen, warum Reflexion schlecht beraten ist, schauen wir uns die Dokumentation an :

Strukturwerte sind zutiefst gleich, wenn die entsprechenden Felder, sowohl exportiert als auch nicht exportiert, sehr gleich sind.

....

Zahlen, Bools, Strings und Kanäle - sind zutiefst gleich, wenn sie mit dem Operator == von Go gleich sind.

Wenn wir zwei time.TimeWerte derselben UTC-Zeit vergleichen, t1 == t2ist dies falsch, wenn ihre Metadaten-Zeitzone unterschiedlich ist.

go-cmpsucht nach der Equal()Methode und verwendet diese, um die Zeiten korrekt zu vergleichen.

Beispiel:

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
Ja genau! Beim Schreiben von Tests ist es sehr wichtig, diese zu verwenden go-cmpund nicht reflect.
Kevin Minehart

Leider funktionieren weder Reflect noch CMP für den Vergleich einer Struktur mit einem Teil von Zeigern auf Strukturen. Es möchte immer noch, dass die Zeiger gleich sind.
Violaman

2
@ GeneralLeeSpeaking das stimmt nicht. Aus der cmp-Dokumentation : "Zeiger sind gleich, wenn die zugrunde liegenden Werte, auf die sie zeigen, ebenfalls gleich sind"
Ilia Choly

Laut der CMP-Dokumentation wird die Verwendung von CMP nur beim Schreiben von Tests empfohlen, da es zu Panik kommen kann, wenn Objekte nicht vergleichbar sind.
Martin

17

So rollen Sie Ihre eigene Funktion: 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
Ich würde das Hinzufügen empfehlen if len(a.Z) != len(b.Z) || len(a.M) != len(b.M) { return false }, da eines von ihnen zusätzliche Felder haben könnte.
OneOfOne

Alle Strukturinformationen sind zur Kompilierungszeit bekannt. Es ist eine Schande, dass der Compiler dieses schwere Heben in keiner Weise ausführen kann.
Rick-777

3
@ Rick-777 Für Slices ist einfach kein Vergleich definiert. So wollten es die Sprachdesigner. Es ist nicht so einfach zu definieren wie beispielsweise der Vergleich einfacher Ganzzahlen. Sind Slices gleich, wenn sie dieselben Elemente in derselben Reihenfolge enthalten? Was aber, wenn sich ihre Kapazitäten unterscheiden? Etc.
Justinas

1
if & a == & b {return true} Dies wird niemals als true ausgewertet, wenn die zu vergleichenden Parameter als Wert übergeben werden.
Sean

4

Seit Juli 2017 können Sie cmp.Equalmit cmpopts.IgnoreFieldsOption verwenden.

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
}

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.