두 개의 구조체, 슬라이스 또는 맵이 동일한 지 비교하는 방법은 무엇입니까?


131

두 개의 구조체, 슬라이스 및 맵이 동일한 지 확인하고 싶습니다.

하지만 다음 코드에 문제가 있습니다. 관련 라인에서 내 의견을 참조하십시오.

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 [string] int를 포함하는 구조체를 비교할 수 없음)', 이는 구조체가 그의 정의 내에 int []가없는 경우에 발생합니다
Victor

답변:



69

reflect.DeepEqual 질문에서와 같이 두 개의 유사한 구조체를 비교하는 데 종종 잘못 사용됩니다.

cmp.Equal 구조체를 비교하는 데 더 좋은 도구입니다.

반사가 잘못된 이유를 확인하려면 문서를 살펴 보겠습니다 .

내 보낸 필드와 내 보내지 않은 해당 필드가 모두 동일한 경우 구조 값은 완전히 동일합니다.

....

숫자, 부울, 문자열 및 채널-Go의 == 연산자를 사용하여 동일하면 완전히 동일합니다.

time.Time동일한 UTC 시간의 두 값을 비교 t1 == t2하면 메타 데이터 시간대가 다르면 false가됩니다.

go-cmpEqual()방법을 찾고 이를 사용하여 시간을 올바르게 비교합니다.

예:

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
맞아요! 테스트를 작성할 때를 사용하는 것이 go-cmp아니라 사용하는 것이 매우 중요합니다 reflect.
케빈 Minehart

불행히도 구조체에 대한 포인터 조각과 구조체를 비교하는 데 reflect 또는 cmp가 작동하지 않습니다. 여전히 포인터가 동일하기를 원합니다.
Violaman

2
@GeneralLeeSpeaking 사실이 아닙니다. 로부터 CMP 문서 "가 가리키는 기본 값도 동일 할 경우 포인터는 동일하다"
일리아 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 2014-07-02

모든 구조 정보는 컴파일 타임에 알려져 있습니다. 컴파일러가 어떤 식 으로든이 무거운 작업을 수행 할 수 없다는 것은 부끄러운 일입니다.
Rick-777

3
@ Rick-777 슬라이스에 대해 정의 된 비교가 없습니다. 이것이 언어 디자이너가 원했던 방식입니다. 예를 들어 간단한 정수의 비교처럼 정의하는 것은 간단하지 않습니다. 동일한 순서로 동일한 요소를 포함하는 슬라이스는 동일합니까? 하지만 용량이 다르면 어떨까요? 기타
justinas jul.

1
if & a == & b {return true} 비교할 매개 변수가 값으로 전달되면 true로 평가되지 않습니다.

4

20177 월 부터 옵션 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
}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.