在Golang中,基于另一个切片对结构体切片进行过滤

9
在下面的示例代码中,我有一些用户在manySimpleUsers中,我想根据用户名从manyFullUsers中删除这些用户。
如果我使用嵌套的for...range循环来完成它,那么需要很多循环来过滤所有元素,特别是当两个切片中都有大量元素时。
在Go中,最好的方法是什么?
package main

import "fmt"

func main() {
    fmt.Println("Hello, playground")

    type FullUser struct {
        UserName  string
        UserEmail string
    }

    manyFullUsers := []FullUser{{"foo", "foo@jawohl.com"},
        {"bar", "bar@jawohl.com"},
        {"baz", "baz@jawohl.com"}}

    type SimpleUser struct {
        UserName string
    }

    manySimpleUsers := []SimpleUser{{"foo"}, {"bar"}}

    fmt.Println(manyFullUsers)
    fmt.Println(manySimpleUsers)
}
2个回答

12
创建一个映射,然后使用它进行过滤。
func filterByUserName(fu []FullUser, su []SimpleUser) (out []FullUser) {
    f := make(map[string]struct{}, len(su))
    for _, u := range su {
        f[u.UserName] = struct{}{}
    }
    for _, u := range fu {
        if _, ok := f[u.UserName]; ok {
            out = append(out, u)
        }
    }
    return
}

playground


顺便提一下,你可以使用布尔映射来节省一些字节:f := make(map[string]bool, len(su)) // 稍后,在循环内部: f[u.UserName] = true - coquin
2
@coquin struct{}使用0字节,而bool类型至少使用1个字节。 - OneOfOne
谢谢!我可以直接将数据解析成 map[string]struct{},因为“filtering” JSON 的格式在我的控制下。我还需要做与你所写的相反的操作,因此我只需使用 !ok 就可以了。再次感谢! - pardonmemiss

1
你可以使用我创建的https://github.com/ledongthuc/goterators中的Filter()和Exist()函数来重用聚合和转换功能。

enter image description here

    filteredFullUsers := goterators.Filter(manyFullUsers, func(item FullUser) bool {
        return !goterators.Exist(manySimpleUsers, SimpleUser{item.UserName})
    })

您也可以从许多SimpleUsers中构建一个地图,这将有助于优化搜索用户名。
此库需要Go 1.18才能使用,以支持您想要使用的通用+动态类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接