Check if a value is in a list

2019-01-28 00:29发布

问题:

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?

回答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}