Does Go have "if x in" construct similar to Python?

If StatementGo

If Statement Problem Overview


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: ...

If Statement Solutions


Solution 1 - If Statement

There is no built-in operator to do it in Go. You need to iterate over the array. You can write your own function to do it, like this:

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

Or in Go 1.18 or newer, you can use slices.Contains (from golang.org/x/exp/slices).

If you want to be able to check for membership without iterating over the whole list, you need to use a map instead of an array or slice, like this:

visitedURL := map[string]bool {
    "http://www.google.com": true,
    "https://paypal.com": true,
}
if visitedURL[thisSite] {
    fmt.Println("Already been here.")
}

Solution 2 - If Statement

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
}

Solution 3 - If Statement

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

Solution 4 - If Statement

Just had a 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 nonexistent 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.

Solution 5 - If Statement

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

Solution 6 - If Statement

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.

Solution 7 - If Statement

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1529891View Question on Stackoverflow
Solution 1 - If StatementandybalholmView Answer on Stackoverflow
Solution 2 - If StatementsebestView Answer on Stackoverflow
Solution 3 - If StatementAlexTTView Answer on Stackoverflow
Solution 4 - If StatementIgorView Answer on Stackoverflow
Solution 5 - If StatementRobert WeberView Answer on Stackoverflow
Solution 6 - If StatementIcarusNMView Answer on Stackoverflow
Solution 7 - If StatementnobledView Answer on Stackoverflow