HiveBrain v1.2.0
Get Started
← Back to all entries
snippetgoCritical

How to search for an element in a golang slice

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
searchhowsliceforelementgolang

Problem

I have a slice of structs.

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)


Here is the output of this:

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


How can I search this array to get the element where key="key1"?

Solution

Starting with Go 1.21, there's the slices package in the standard lib with a generic search function named slices.IndexFunc():

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


IndexFunc returns the first index i satisfying f(s[i]), or -1 if none do.

Using that:

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


Try it on the Go Playground.

For older versions read on.

Before Go 1.21, it was part of an external repository: golang.org/x/exp/slices package which contains a generic "find" function named slices.IndexFunc():

Prior to Go 1.18 and for a faster alternative, read on:

With a simple for loop:

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


Note that since element type of the slice is a struct (not a pointer), this may be inefficient if the struct type is "big" as the loop will copy each visited element into the loop variable.

It would be faster to use a range loop just on the index, this avoids copying the elements:

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


Notes:

It depends on your case whether multiple configs may exist with the same key, but if not, you should break out of the loop if a match is found (to avoid searching for others).

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


Also if this is a frequent operation, you should consider building a map from it which you can simply index, e.g.

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

Code Snippets

func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int
idx := slices.IndexFunc(myconfig, func(c Config) bool { return c.Key == "key1" })
for _, v := range myconfig {
    if v.Key == "key1" {
        // Found!
    }
}
for i := range myconfig {
    if myconfig[i].Key == "key1" {
        // Found!
    }
}
for i := range myconfig {
    if myconfig[i].Key == "key1" {
        // Found!
        break
    }
}

Context

Stack Overflow Q#38654383, score: 313

Revisions (0)

No revisions yet.