4

许多其他语言已经回答了这个问题。在带有简单地图(无嵌套)的 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 { // invalid: value == nil
            return false
        }
    }

    return true
}

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

毕竟我可以更好地完成同样的工作吗?

4

3 回答 3

2

Value.MapIndex()返回 areflect.Value这是一个结构,并且nil不是结构的有效值。您不能将 struct 值与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
于 2021-06-09T09:16:12.913 回答
2

我想要一个通用的方法。

如果你能等到 Go 1.18(将于 2022 年 2 月发布),该版本计划将泛型引入语言,你将能够编写这样的泛型函数;见下文和在这个操场上。这样就没有必要诉诸反思,这通常是不鼓励的。

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
}

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))
}

输出:

true
false

NaN不过,关于 apply的常见警告。

于 2021-06-09T09:55:33.577 回答
1

如果有人需要,这是可行的解决方案:

// 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("%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
}
于 2021-06-09T13:18:08.897 回答