[if-statement] Does Go have "if x in" construct similar to Python?

Without iterating over the entire array, how can I check if x in array using Go? Does the language have a construct?

Like Python: if "x" in array: ...

This question is related to if-statement go

The answer is


The above example using sort is close, but in the case of strings simply use SearchString:

files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"}
target := "Makefile"
sort.Strings(files)
i := sort.SearchStrings(files, target)
if i < len(files) && files[i] == target {
    fmt.Printf("found \"%s\" at files[%d]\n", files[i], i)
}

https://golang.org/pkg/sort/#SearchStrings


This is quote from the book "Programming in Go: Creating Applications for the 21st Century":

Using a simple linear search like this is the only option for unsorted data and is fine for small slices (up to hundreds of items). But for larger slices—especially if we are performing searches repeatedly—the linear search is very inefficient, on average requiring half the items to be compared each time.

Go provides a sort.Search() method which uses the binary search algorithm: This requires the comparison of only log2(n) items (where n is the number of items) each time. To put this in perspective, a linear search of 1000000 items requires 500000 comparisons on average, with a worst case of 1000000 comparisons; a binary search needs at most 20 comparisons, even in the worst case.

files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"}
target := "Makefile"
sort.Strings(files)
i := sort.Search(len(files),
    func(i int) bool { return files[i] >= target })
if i < len(files) && files[i] == target {
    fmt.Printf("found \"%s\" at files[%d]\n", files[i], i)
}

https://play.golang.org/p/UIndYQ8FeW


Another solution if the list contains static values.

eg: checking for a valid value from a list of valid values:

func IsValidCategory(category string) bool {
    switch category {
    case
        "auto",
        "news",
        "sport",
        "music":
        return true
    }
    return false
}

Another option is using a map as a set. You use just the keys and having the value be something like a boolean that's always true. Then you can easily check if the map contains the key or not. This is useful if you need the behavior of a set, where if you add a value multiple times it's only in the set once.

Here's a simple example where I add random numbers as keys to a map. If the same number is generated more than once it doesn't matter, it will only appear in the final map once. Then I use a simple if check to see if a key is in the map or not.

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    var MAX int = 10

    m := make(map[int]bool)

    for i := 0; i <= MAX; i++ {
        m[rand.Intn(MAX)] = true
    }

    for i := 0; i <= MAX; i++ {
        if _, ok := m[i]; ok {
            fmt.Printf("%v is in map\n", i)
        } else {
            fmt.Printf("%v is not in map\n", i)
        }
    }
}

Here it is on the go playground


Just had similar question and decided to try out some of the suggestions in this thread.

I've benchmarked best and worst case scenarios of 3 types of lookup:

  • using a map
  • using a list
  • using a switch statement

here's the function code:

func belongsToMap(lookup string) bool {
list := map[string]bool{
    "900898296857": true,
    "900898302052": true,
    "900898296492": true,
    "900898296850": true,
    "900898296703": true,
    "900898296633": true,
    "900898296613": true,
    "900898296615": true,
    "900898296620": true,
    "900898296636": true,
}
if _, ok := list[lookup]; ok {
    return true
} else {
    return false
}
}


func belongsToList(lookup string) bool {
list := []string{
    "900898296857",
    "900898302052",
    "900898296492",
    "900898296850",
    "900898296703",
    "900898296633",
    "900898296613",
    "900898296615",
    "900898296620",
    "900898296636",
}
for _, val := range list {
    if val == lookup {
        return true
    }
}
return false
}

func belongsToSwitch(lookup string) bool {
switch lookup {
case
    "900898296857",
    "900898302052",
    "900898296492",
    "900898296850",
    "900898296703",
    "900898296633",
    "900898296613",
    "900898296615",
    "900898296620",
    "900898296636":
    return true
}
return false
}

best case scenarios pick the first item in lists, worst case ones use nonexistant value.

here are the results:

BenchmarkBelongsToMapWorstCase-4 2000000 787 ns/op BenchmarkBelongsToSwitchWorstCase-4 2000000000 0.35 ns/op BenchmarkBelongsToListWorstCase-4 100000000 14.7 ns/op BenchmarkBelongsToMapBestCase-4 2000000 683 ns/op BenchmarkBelongsToSwitchBestCase-4 100000000 10.6 ns/op BenchmarkBelongsToListBestCase-4 100000000 10.4 ns/op

Switch wins all the way, worst case is surpassingly quicker than best case. Maps are the worst and list is closer to switch.

So the moral is: If you have a static, reasonably small list, switch statement is the way to go.


This is as close as I can get to the natural feel of Python's "in" operator. You have to define your own type. Then you can extend the functionality of that type by adding a method like "has" which behaves like you'd hope.

package main

import "fmt"

type StrSlice []string

func (list StrSlice) Has(a string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}

func main() {
    var testList = StrSlice{"The", "big", "dog", "has", "fleas"}

    if testList.Has("dog") {
        fmt.Println("Yay!")
    }
}

I have a utility library where I define a few common things like this for several types of slices, like those containing integers or my own other structs.

Yes, it runs in linear time, but that's not the point. The point is to ask and learn what common language constructs Go has and doesn't have. It's a good exercise. Whether this answer is silly or useful is up to the reader.