【问题标题】:How to search for an element in a golang slice如何在 golang 切片中搜索元素
【发布时间】:2016-12-03 21:49:21
【问题描述】:

我有一片结构。

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" 所在的元素?

【问题讨论】:

  • 由于您的 Config 结构看起来像一个简单的映射,我想指出您可以将任何 JSON 数据解码为 map[string]interface{}。如果您有兴趣,请查看this official blog post

标签: go struct slice


【解决方案1】:

这是一个基于@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")
    }
}

【讨论】:

    【解决方案2】:

    你可以使用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

    【讨论】:

    • 在比较之前,您应该先检查返回的索引是否超出范围。
    【解决方案3】:

    正如其他人评论的那样,您可以使用匿名函数编写自己的程序来解决此问题。

    我用了两种方法解决:

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

    【讨论】:

      【解决方案4】:

      没有用于此的库函数。您必须自己编写代码。

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

      【讨论】:

        【解决方案5】:

        您可以通过将结构 KeyValue 组件与映射上的虚构键和值部分匹配来将结构保存到映射中:

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

        然后使用 golang comma ok 成语,您可以测试密钥是否存在:

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

        【讨论】:

          【解决方案6】:

          使用简单的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
          }
          

          【讨论】:

          • 谢谢我也可以随意使用指针。你认为在这里使用指向结构的指针会更快吗?我们可以有一个指针数组吗?
          • @love2code 是的,您也可以使用指针切片,或者如果您遍历索引(如我的第二个示例),则不会复制值。由你决定。
          • 好的,我创建了var myconfig []*Config 并使用了您的第一种方法。我希望这是最好的选择性能明智
          • @love2code 性能方面的最佳选择是从中构建一个可以索引的地图。请参阅编辑后的答案。
          猜你喜欢
          • 2016-03-10
          • 2016-09-16
          • 2012-09-23
          • 2021-09-21
          • 2020-08-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-15
          相关资源
          最近更新 更多