如何搜索一个元素在一个戈兰切片

我有一段结构。

type Config struct {
Key string
Value string
}


// I form a slice of the above struct
var myconfig []Config


// unmarshal a response body into the above slice
if err := json.Unmarshal(respbody, &myconfig); err != nil {
panic(err)
}


fmt.Println(config)

下面是结果:

[{key1 test} {web/key1 test2}]

如何搜索这个数组以获得 key="key1"所在的元素?

242750 次浏览

从增加泛型支持的 Go 1.18开始,有一个包含一个名为 slices.IndexFunc()的泛型“ find”函数的 golang.org/x/exp/slices包:

func IndexFunc[E any](s []E, f func(E) bool) int

IndexFunc 返回满足 f (s [ i ])的第一个索引 i,如果没有,返回 -1。

利用这一点:

idx := slices.IndexFunc(myconfig, func(c Config) bool { return c.Key == "key1" })

去游乐场试试。

在 Go 1.18之前,如果想要更快的选择,请继续阅读:

通过一个简单的 for循环:

for _, v := range myconfig {
if v.Key == "key1" {
// Found!
}
}

请注意,由于切片的元素类型是 struct(而不是指针) ,如果结构类型是“大的”,这可能是低效的,因为循环将把每个访问过的元素复制到循环变量中。

仅在索引上使用 range循环会更快,这样可以避免复制元素:

for i := range myconfig {
if myconfig[i].Key == "key1" {
// Found!
}
}

备注:

这取决于使用相同 key的多个配置是否可能存在,但如果不存在,那么如果找到匹配,则应该将 break排除在循环之外(以避免搜索其他配置)。

for i := range myconfig {
if myconfig[i].Key == "key1" {
// Found!
break
}
}

此外,如果这是一个频繁的操作,您应该考虑构建一个 map,您可以简单地从它索引,例如。

// Build a config map:
confMap := map[string]string{}
for _, v := range myconfig {
confMap[v.Key] = v.Value
}


// And then to find values by key:
if v, ok := confMap["key1"]; ok {
// Found
}

您可以通过将 struct KeyValue组件与它们在 map 上的虚拟键和值部分匹配,将 struct 保存到 map 中:

mapConfig := map[string]string{}
for _, v := range myconfig {
mapConfig[v.Key] = v.Value
}

然后使用 golang 逗号 OK成语,您可以测试关键存在:

if v, ok := mapConfig["key1"]; ok {
fmt.Printf("%s exists", v)
}

这里没有库函数,你必须自己编码。

for _, value := range myconfig {
if value.Key == "key1" {
// logic
}
}

工作代码: https://play.golang.org/p/IJIhYWROP _

package main


import (
"encoding/json"
"fmt"
)


func main() {
type Config struct {
Key   string
Value string
}


var respbody = []byte(`[
{"Key":"Key1", "Value":"Value1"},
{"Key":"Key2", "Value":"Value2"}
]`)


var myconfig []Config


err := json.Unmarshal(respbody, &myconfig)
if err != nil {
fmt.Println("error:", err)
}


fmt.Printf("%+v\n", myconfig)


for _, v := range myconfig {
if v.Key == "Key1" {
fmt.Println("Value: ", v.Value)
}
}


}

可以使用 sort.Slice()sort.Search()

type Person struct {
Name string
}


func main() {
crowd := []Person\{\{"Zoey"}, {"Anna"}, {"Benni"}, {"Chris"}}


sort.Slice(crowd, func(i, j int) bool {
return crowd[i].Name <= crowd[j].Name
})


needle := "Benni"
idx := sort.Search(len(crowd), func(i int) bool {
return string(crowd[i].Name) >= needle
})


if idx < len(crowd) && crowd[idx].Name == needle {
fmt.Println("Found:", idx, crowd[idx])
} else {
fmt.Println("Found noting: ", idx)
}
}

见: https://play.golang.org/p/47OPrjKb0g_c

正如其他人之前评论的那样,你可以用匿名函数编写自己的过程来解决这个问题。

我用了两种方法来解决这个问题:

func Find(slice interface{}, f func(value interface{}) bool) int {
s := reflect.ValueOf(slice)
if s.Kind() == reflect.Slice {
for index := 0; index < s.Len(); index++ {
if f(s.Index(index).Interface()) {
return index
}
}
}
return -1
}

举例说明:

type UserInfo struct {
UserId          int
}


func main() {
var (
destinationList []UserInfo
userId      int = 123
)
    

destinationList = append(destinationList, UserInfo {
UserId          : 23,
})
destinationList = append(destinationList, UserInfo {
UserId          : 12,
})
    

idx := Find(destinationList, func(value interface{}) bool {
return value.(UserInfo).UserId == userId
})
    

if idx < 0 {
fmt.Println("not found")
} else {
fmt.Println(idx)
}
}

第二种方法计算成本较低:

func Search(length int, f func(index int) bool) int {
for index := 0; index < length; index++ {
if f(index) {
return index
}
}
return -1
}

举例说明:

type UserInfo struct {
UserId          int
}


func main() {
var (
destinationList []UserInfo
userId      int = 123
)
    

destinationList = append(destinationList, UserInfo {
UserId          : 23,
})
destinationList = append(destinationList, UserInfo {
UserId          : 123,
})
    

idx := Search(len(destinationList), func(index int) bool {
return destinationList[index].UserId == userId
})
    

if  idx < 0 {
fmt.Println("not found")
} else {
fmt.Println(idx)
}
}

下面是一个基于@Tarion 回答的简单函数。

func findProgram (programs []Program, id uint) (Program, error) {
sort.Slice(programs, func(i, j int) bool {
return programs[i].ID <= programs[j].ID
})


idx := sort.Search(len(programs), func(i int) bool {
return programs[i].ID >= id
})


if idx < len(programs) && programs[idx].ID == id {
return programs[idx], nil
} else {
return Program{}, fmt.Errorf("program not found")
}
}

如果有人像我一样来自 Java 或 C # ,我最终会这么做:

type Person struct {
Name string
Age  int
}
// create slice of people
var people []Person = []Person{
{"Tono", 33},
{"Regina", 25},
{"Bob", 40},
}


// find person where its Name equals to Bob <------------------
bob := FirstOrDefault(people, func(p *Person) bool { return p.Name == "Bob" })


if bob != nil {
fmt.Printf("Found bob: %v \n", *bob)
} else {
fmt.Println("could not find bob")
}


peopleOlderThan30 := Where(people, func(p *Person) bool { return p.Age > 30 })


fmt.Println("People older than 30 are:")
for _, element := range peopleOlderThan30 {
fmt.Println(*element)
}

我可以在这些函数的帮助下运行代码:

func FirstOrDefault[T any](slice []T, filter func(*T) bool) (element *T) {


for i := 0; i < len(slice); i++ {
if filter(&slice[i]) {
return &slice[i]
}
}


return nil
}


func Where[T any](slice []T, filter func(*T) bool) []*T {


var ret []*T = make([]*T, 0)


for i := 0; i < len(slice); i++ {
if filter(&slice[i]) {
ret = append(ret, &slice[i])
}
}


return ret
}