【问题标题】:Check if a map is subset of another map检查一个地图是否是另一个地图的子集
【发布时间】:2021-08-26 06:24:55
【问题描述】:

这个问题已经用许多其他语言回答。在带有简单地图(无嵌套)的 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 返回类型的零值,并且无法将其与任何内容进行比较。

毕竟我能把同样的工作做得更好吗?

【问题讨论】:

    标签: dictionary go generics reflection go-map


    【解决方案1】:

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

    Value.MapIndex() 声明:

    如果在 map 中没有找到 key 或者 v 表示 nil map,则返回零值。

    所以要判断是否在映射中找不到键,请检查返回的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
    

    【讨论】:

    • 这似乎不适用于keys of the map are different的情况。
    • 在上一条评论中使用!v2.isValid()通过我的测试用例
    • @moluzhui 是的,你是对的。已修复,谢谢。
    【解决方案2】:

    我想要一个通用方法。

    如果你能等到 Go 1.18(将于 2022 年 2 月发布),该版本计划将泛型引入语言,你将能够编写这样的泛型函数;见下文和Playground。这样就无需诉诸反思,which is generally discouraged

    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
    

    The usual caveats about NaN 申请,不过。

    【讨论】:

    • 简洁的解决方案。我将在项目中添加一个带有您的答案链接的 TODO 并等待 go 1.18
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-26
      • 2012-09-28
      • 2010-09-24
      • 1970-01-01
      相关资源
      最近更新 更多