检查一个地图是否是另一个地图的子集。

huangapple go评论82阅读模式
英文:

Check if a map is subset of another map

问题

这个问题已经在许多其他语言中得到了回答。在使用简单映射(没有嵌套)的golang中,如何判断一个映射是否是另一个映射的子集。例如:map[string]string{"a": "b", "e": "f"}map[string]string{"a": "b", "c": "d", "e": "f"}的子集。我想要一个通用的方法。我的代码如下:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := map[string]string{"a": "b", "c": "d", "e": "f"}
	b := map[string]string{"a": "b", "e": "f"}
	c := IsMapSubset(a, b)
	fmt.Println(c)
}

func IsMapSubset(mapSet interface{}, mapSubset interface{}) bool {

	mapSetValue := reflect.ValueOf(mapSet)
	mapSubsetValue := reflect.ValueOf(mapSubset)

	if mapSetValue.Kind() != reflect.Map || mapSubsetValue.Kind() != reflect.Map {
		return false
	}
	if reflect.TypeOf(mapSetValue) != reflect.TypeOf(mapSubsetValue) {
		return false
	}
	if len(mapSubsetValue.MapKeys()) == 0 {
		return true
	}

	iterMapSubset := mapSubsetValue.MapRange()

	for iterMapSubset.Next() {
		k := iterMapSubset.Key()
		v := iterMapSubset.Value()

		if value := mapSetValue.MapIndex(k); value == nil || v != value.Interface() {
			return false
		}
	}

	return true
}

当我想要检查子集映射的键是否存在于集合映射中时,MapIndex返回类型的零值,使其无法与任何值进行比较。

总的来说,我能否更好地完成同样的工作?

英文:

This question is already answered in many other languages. In golang with simple maps (no nesting) how to find out if a map is subset of another. for example: map[string]string{"a": "b", "e": "f"} is subset of map[string]string{"a": "b", "c": "d", "e": "f"}. I want a generic method. My code:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := map[string]string{"a": "b", "c": "d", "e": "f"}
	b := map[string]string{"a": "b", "e": "f"}
	c := IsMapSubset(a, b)
	fmt.Println(c)
}

func IsMapSubset(mapSet interface{}, mapSubset interface{}) bool {

	mapSetValue := reflect.ValueOf(mapSet)
	mapSubsetValue := reflect.ValueOf(mapSubset)

	if mapSetValue.Kind() != reflect.Map || mapSubsetValue.Kind() != reflect.Map {
		return false
	}
	if reflect.TypeOf(mapSetValue) != reflect.TypeOf(mapSubsetValue) {
		return false
	}
	if len(mapSubsetValue.MapKeys()) == 0 {
		return true
	}

	iterMapSubset := mapSubsetValue.MapRange()

	for iterMapSubset.Next() {
		k := iterMapSubset.Key()
		v := iterMapSubset.Value()

		if value := mapSetValue.MapIndex(k); value == nil || v != value { // invalid: value == nil
			return false
		}
	}

	return true
}

When I want to check if subset map key exists in set map, MapIndex returns zero value of type and make it impossible to compare it with anything.

Afterall can I do the same job better?

答案1

得分: 5

我想要一个通用的方法。

现在Go 1.18和泛型已经发布,你可以编写这样一个通用函数;请参考下面的代码和Playground中的示例。不需要使用反射来实现这个功能,反射通常是不鼓励使用的。

package main

import "fmt"

func IsMapSubset[K, V comparable](m, sub map[K]V) bool {
    if len(sub) > len(m) {
        return false
    }
    for k, vsub := range sub {
        if vm, found := m[k]; !found || vm != vsub {
            return false
        }
    }
    return true
}

type MyMap map[string]string

func main() {
    a := map[string]string{"a": "b", "c": "d", "e": "f"}
    b := map[string]string{"a": "b", "e": "f"}
    c := map[string]string{"a": "b", "e": "g"}
    fmt.Println(IsMapSubset(a, b))
    fmt.Println(IsMapSubset(a, c))
    fmt.Println(IsMapSubset(MyMap(a), c))
}

输出:

true
false

不过,关于NaN的常规注意事项仍然适用。

英文:

> I want a generic method.

Now that Go 1.18 and generics are here, you can write such a generic function; see below and in this Playground. Reflection, which is generally discouraged, is not required to implement this functionality.

package main

import "fmt"

func IsMapSubset[K, V comparable](m, sub map[K]V) bool {
	if len(sub) > len(m) {
		return false
	}
	for k, vsub := range sub {
		if vm, found := m[k]; !found || vm != vsub {
			return false
		}
	}
	return true
}

type MyMap map[string]string

func main() {
	a := map[string]string{"a": "b", "c": "d", "e": "f"}
	b := map[string]string{"a": "b", "e": "f"}
	c := map[string]string{"a": "b", "e": "g"}
	fmt.Println(IsMapSubset(a, b))
	fmt.Println(IsMapSubset(a, c))
	fmt.Println(IsMapSubset(MyMap(a), c))
}

Output:

true
false

The usual caveats about NaN apply, though.

答案2

得分: 2

Value.MapIndex() 返回一个 reflect.Value,它是一个结构体,而 nil 不是结构体的有效值。你不能将结构体值与 nil 进行比较。

Value.MapIndex() 的说明如下:

> 如果在映射中找不到键或者 v 表示一个空映射,则返回零值。

因此,要判断是否在映射中找不到键,可以检查返回的 reflect.Value 是否为其零值。为此,可以使用 Value.IsValid() 方法。

你也不能(也不应该)比较 reflect.Value 值。相反,应该使用 Value.Interface() 获取其包装值,并进行比较。

if v2 := mapSetValue.MapIndex(k); !v2.IsValid() || v.Interface() != v2.Interface() {
	return false
}

进行测试:

a := map[string]string{"a": "b", "c": "d", "e": "f"}
b := map[string]string{"a": "b", "e": "f"}
fmt.Println(IsMapSubset(a, b))

c := map[string]string{"a": "b", "e": "X"}
fmt.Println(IsMapSubset(a, c))

输出结果为(在 Go Playground 上尝试):

true
false
英文:

Value.MapIndex() returns a reflect.Value which is a struct, and nil is not a valid value for structs. You can't compare a struct value to nil.

Value.MapIndex() states that:

> It returns the zero Value if key is not found in the map or if v represents a nil map.

So to tell if the key was not found in the map, check if the returned reflect.Value is its zero value. For that you may use the Value.IsValid() method.

You also can't (shouldn't) compare reflect.Value values. Instead obtain their wrapped value using Value.Interface(), and compare those.

if v2 := mapSetValue.MapIndex(k); !v2.IsValid() || v.Interface() != v2.Interface() {
	return false
}

Testing it:

a := map[string]string{"a": "b", "c": "d", "e": "f"}
b := map[string]string{"a": "b", "e": "f"}
fmt.Println(IsMapSubset(a, b))

c := map[string]string{"a": "b", "e": "X"}
fmt.Println(IsMapSubset(a, c))

Output will be (try it on the Go Playground):

true
false

答案3

得分: 1

这是一个工作的解决方案,如果有人需要的话:

// IsMapSubset函数判断mapSubset是否是mapSet的子集,如果是则返回true,否则返回false
func IsMapSubset(mapSet interface{}, mapSubset interface{}) bool {

mapSetValue := reflect.ValueOf(mapSet)
mapSubsetValue := reflect.ValueOf(mapSubset)

if fmt.Sprintf("%T", mapSet) != fmt.Sprintf("%T", mapSubset) {
    return false
}

if len(mapSetValue.MapKeys()) < len(mapSubsetValue.MapKeys()) {
    return false
}

if len(mapSubsetValue.MapKeys()) == 0 {
    return true
}

iterMapSubset := mapSubsetValue.MapRange()

for iterMapSubset.Next() {
    k := iterMapSubset.Key()
    v := iterMapSubset.Value()

    value := mapSetValue.MapIndex(k)

    if !value.IsValid() || v.Interface() != value.Interface() {
        return false
    }
}

return true

}

英文:

This is the working solution in case anyone needs:

// IsMapSubset returns true if mapSubset is a subset of mapSet otherwise false
func IsMapSubset(mapSet interface{}, mapSubset interface{}) bool {

	mapSetValue := reflect.ValueOf(mapSet)
	mapSubsetValue := reflect.ValueOf(mapSubset)

	if fmt.Sprintf(&quot;%T&quot;, mapSet) != fmt.Sprintf(&quot;%T&quot;, mapSubset) {
		return false
	}

	if len(mapSetValue.MapKeys()) &lt; len(mapSubsetValue.MapKeys()) {
		return false
	}

	if len(mapSubsetValue.MapKeys()) == 0 {
		return true
	}

	iterMapSubset := mapSubsetValue.MapRange()

	for iterMapSubset.Next() {
		k := iterMapSubset.Key()
		v := iterMapSubset.Value()

		value := mapSetValue.MapIndex(k)

		if !value.IsValid() || v.Interface() != value.Interface() {
			return false
		}
	}

	return true
}

huangapple
  • 本文由 发表于 2021年6月9日 17:04:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/67900919.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定