จะเปรียบเทียบได้อย่างไรว่าโครงสร้างสองชิ้นหรือแผนที่เท่ากันหรือไม่?


132

ฉันต้องการตรวจสอบว่าโครงสร้างสองส่วนและแผนที่เท่ากันหรือไม่

แต่ฉันพบปัญหากับรหัสต่อไปนี้ ดูความคิดเห็นของฉันที่บรรทัดที่เกี่ยวข้อง

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 ยัง 'การดำเนินการที่ไม่ถูกต้อง: t2 == t1 (ไม่สามารถเปรียบเทียบโครงสร้างที่มี map [สตริง] int ได้)' ซึ่งจะเกิดขึ้นหากโครงสร้างไม่มี int [] ภายในคำจำกัดความของเขา
วิกเตอร์

คำตอบ:


158

คุณสามารถใช้reflect.DeepEqualหรือคุณสามารถใช้ฟังก์ชันของคุณเองได้ (ซึ่งประสิทธิภาพจะดีกว่าการใช้การสะท้อน):

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

m1 := map[string]int{   
    "a":1,
    "b":2,
}
m2 := map[string]int{   
    "a":1,
    "b":2,
}
fmt.Println(reflect.DeepEqual(m1, m2))

71

reflect.DeepEqual มักจะใช้อย่างไม่ถูกต้องในการเปรียบเทียบสองอย่างเช่นโครงสร้างเช่นเดียวกับในคำถามของคุณ

cmp.Equal เป็นเครื่องมือที่ดีกว่าในการเปรียบเทียบโครงสร้าง

หากต้องการดูว่าเหตุใดการสะท้อนจึงไม่แนะนำให้ดูเอกสารประกอบ :

ค่าโครงสร้างจะเท่ากันอย่างมากหากฟิลด์ที่เกี่ยวข้องทั้งที่ส่งออกและไม่ได้ส่งออกมีค่าเท่ากันอย่างมาก

....

ตัวเลขบูลสตริงและแชนเนล - จะเท่ากันอย่างมากหากเท่ากันโดยใช้โอเปอเรเตอร์ Go's ==

หากเราเปรียบเทียบtime.Timeค่าสองค่าของเวลา UTC เดียวกันt1 == t2จะเป็นเท็จหากเขตเวลาข้อมูลเมตาต่างกัน

go-cmpมองหาEqual()วิธีการและใช้เพื่อเปรียบเทียบเวลาอย่างถูกต้อง

ตัวอย่าง:

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

10
ใช่แน่นอน! เมื่อมีการเขียนการทดสอบมันเป็นสิ่งสำคัญมากที่จะใช้และไม่ได้go-cmp reflect
Kevin Minehart

น่าเสียดายที่ทั้งสะท้อนหรือ cmp ไม่ทำงานในการเปรียบเทียบโครงสร้างกับตัวชี้กับโครงสร้าง มันยังคงต้องการพอยน์เตอร์เหมือนเดิม
Violaman

2
@GeneralLeeSpeaking นั่นไม่เป็นความจริง จากเอกสาร cmp : "พอยน์เตอร์จะเท่ากันหากค่าพื้นฐานที่ชี้ไปนั้นเท่ากัน"
Ilia Choly

ตามเอกสาร cmp แนะนำให้ใช้ cmp เมื่อเขียนการทดสอบเท่านั้นเพราะอาจทำให้ตกใจหากวัตถุไม่สามารถเทียบเคียงได้
martin

17

นี่คือวิธีที่คุณจะหมุนฟังก์ชันของคุณเอง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
ฉันขอแนะนำให้เพิ่มif len(a.Z) != len(b.Z) || len(a.M) != len(b.M) { return false }เนื่องจากหนึ่งในนั้นอาจมีช่องเพิ่มเติม
OneOfOne

ข้อมูลโครงสร้างทั้งหมดเป็นที่รู้จักในเวลาคอมไพล์ เป็นเรื่องน่าเสียดายที่คอมไพเลอร์ไม่สามารถยกของหนักได้ในบางวิธี
Rick-777

3
@ Rick-777 ไม่มีการเปรียบเทียบที่กำหนดไว้สำหรับชิ้นส่วน นี่คือวิธีที่นักออกแบบภาษาต้องการให้เป็น ไม่ใช่เรื่องง่ายที่จะนิยามเช่นพูดเปรียบเทียบจำนวนเต็มง่ายๆ ชิ้นส่วนเท่ากันหรือไม่หากมีองค์ประกอบเดียวกันในลำดับเดียวกัน แต่ถ้าความสามารถแตกต่างกันล่ะ? ฯลฯ
justinas

1
if & a == & b {return true} ค่านี้จะไม่ประเมินเป็น true หากพารามิเตอร์ที่จะเปรียบเทียบถูกส่งผ่านค่า
ฌอน

4

ตั้งแต่เดือนกรกฎาคม 2017คุณสามารถใช้cmp.Equalกับcmpopts.IgnoreFieldsตัวเลือกได้

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
}

3

หากคุณกำลังเปรียบเทียบพวกเขาในการทดสอบหน่วยเป็นทางเลือกที่มีประโยชน์EqualValuesฟังก์ชั่นในการเป็นพยาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.