'Check if a value is in a list
Does Go have something similar to Python's in
keyword? I want to check if a value is in a list.
For example in Python:
x = 'red'
if x in ['red', 'green', 'yellow', 'blue']:
print "found"
else:
print "not found"
In Go I've come up with using the set idiom but I don't think it's ideal as I have to specify a int value that I'm not using.
x := "red"
valid := map[string]int{"red": 0, "green": 0,"yellow": 0, "blue": 0}
if _, ok := valid[x]; ok {
fmt.Println("found")
} else {
fmt.Println("not found")
}
I understand having an in
keyword is probably related to generics. Is there a way to do this using go generate or something?
Solution 1:[1]
You can use a map[string]bool
as a set. When testing and a key is not in the map, the zero value for bool
is returned which is false
.
So fill the map with the valid values as keys and true
as value. If a tested key-value is in the map, its stored true
value will be the result. If a tested key-value is not in the map, the zero value for the value type is returned which is false
.
Using this, the test becomes this simple:
valid := map[string]bool{"red": true, "green": true, "yellow": true, "blue": true}
if valid[x] {
fmt.Println("found")
} else {
fmt.Println("not found")
}
Try it on the Go Playground (with the variants mentioned below).
This is mentioned in the blog post: Go maps in action: Exploiting zero values
Note:
If you have many valid values, since all the values to be stored in the map are true
, it may be more compact to use a slice to list the valid values and use a for range
loop to initialize your map, something like this:
for _, v := range []string{"red", "green", "yellow", "blue"} {
valid[v] = true
}
Note #2:
If you don't want to go with the for range
loop initialization, you can still optimize it a little by creating an untyped (or bool
-typed) one-letter const
:
const t = true
valid := map[string]bool{"red": t, "green": t, "yellow": t, "blue": t}
Solution 2:[2]
I think map[string]bool
in the other answer is a good option. Another method
is map[string]struct{}
, which uses slightly less memory:
package main
func main() {
x, valid := "red", map[string]struct{}{
"red": {}, "green": {}, "yellow": {}, "blue": {},
}
if _, ok := valid[x]; ok {
println("found")
} else {
println("not found")
}
}
You could also wrap it in a type:
package main
type set map[string]struct{}
func newSet(slice []string) set {
s := make(set)
for _, each := range slice {
s[each] = struct{}{}
}
return s
}
func (s set) has(v string) bool {
_, ok := s[v]
return ok
}
func main() {
x := "red"
if newSet([]string{"red", "green", "yellow", "blue"}).has(x) {
println("found")
} else {
println("not found")
}
}
Solution 3:[3]
Using generics in Go >= 1.18
package slice
func In[Item compareable](items []Item, item Item) bool {
for i := 0; i < len(items); i++ {
if items[i] == item {
return true
}
}
return false
}
package main
import "fmt"
import "slice"
func main() {
fmt.Println(slice.In([]int{1, 2, 3, 4, 5}, 3)
}
true
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Zombo |
Solution 3 | Neil |