在Go中实现切片或结构映射的通用过滤器

我有多个带有“DateLastModified”字段的结构,如下所示:

type Thing struct {
  DateLastModified time.Time
  ...
}

type Thing2 struct {
  DateLastModified time.Time
  ...
}

我有每个这些结构的切片或映射:

things := []Thing{...}
thing2s := []Thing2{...}

//or

things := make(map[string]Thing)
thing2s := make(map[string]Thing2)

我想要做的是过滤他们DateLastModified领域中的每个切片或地图。

这很容易以基本方式实现,但我有兴趣了解更多关于 Go 的知识。

我想知道的是:有没有办法以我可以执行以下操作的方式实现该过滤器:

filteredThings := filterSliceOnTime(things, someTimeToFilterOn)
filtered2Things := filterSliceOnTime(thing2s, someTimeToFilterOn)

//or

filteredThings := filterMapOnTime(things, someTimeToFilterOn)
filtered2Things := filterMapOnTime(thing2s, someTimeToFilterOn)

我想弄清楚的是如何减少冗余代码,因为所有这些结构都具有 DateLastModified 字段。

谢谢!

回答

Go 没有协方差的概念,因此filterXOnTime将无法对不同底层结构的切片[]V或映射进行操作。map[K]VV

带接口

您可以做的是声明一个I公开公共行为的接口,并让两个结构都实现该接口:

type Modifiable interface {
    GetDateLastModified() time.Time
}

type Thing struct {
    DateLastModified time.Time
    ...
}

func (t Thing) GetDateLastModified() time.Time {
    return t.DateLastModified
}

type Thing2 struct {
    DateLastModified time.Time
    ...
}

func (t Thing2) GetDateLastModified() time.Time {
    return t.DateLastModified
}

此时,您可以拥有 的切片和映射I,并且您的过滤器功能可以与这些一起工作(接受和返回):

func filterSliceOnTime(modifiables []Modifiable, someTimeToFilterOn time.Time) []Modifiable { 
    var filtered []Modifiable
    for _, m := range modifiables {
        if m.GetDateLastModified.After(someTimeToFilterOn) {
             filtered = append(filtered, m)
        }
    }
    return filtered
}

这种方法的有效性受到以下事实的限制:为了使用“通用”函数,您必须重新映射[]ThingX[]Modifiable,反之亦然。

带有辅助函数

为了减轻上述解决方案的维护麻烦,您可以改用一个对单个项目进行操作的辅助过滤器功能,这样您就不必来回映射复杂类型:

func checkOne(v Modifiable, filterOn time.Time) bool {
    return v.GetDateLastModified().After(filterOn)
}

func main() {
    a := make(map[string]Thing, 0)
    a["foo"] = Thing{time.Now()}

    filtered := make(map[string]Thing, 0)
    for k, v := range a {
        if checkOne(v, time.Now().Add(-2*time.Hour)) {
             filtered[k] = v
        }
    }
    
    fmt.Println(filtered) // map[foo:{blah}]
}

Go 1.18 并输入参数

随着 Go 1.18(2022 年初)和泛型的引入,您将能够直接使用切片和映射。您仍然需要声明接口来为类型参数提供适当的约束,并且结构仍然必须实现它。

使用当前的草稿设计,可能看起来像:

func filterSliceOnTime[T Modifiable](s []T, filterOn time.Time) []T {
    var filtered []T
    for _, v := range s {
        if v.GetDateLastModified().After(filterOn) {
            filtered = append(filtered, v)
        }
    }
    return filtered
}

Go2 游乐场:https ://go2goplay.golang.org/p/FELhv0NSr5A


以上是在Go中实现切片或结构映射的通用过滤器的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>