3
votes

I have an enumerated list of strings (which are constant, e.g. a list of countries), that I'd like to be able to get the string when providing the enumerated int value, and vice-versa be able to get the enumerated int value when providing the string. This is in order to translate between the two for database transactions.

var MyData = [...]string {
  "string1", // index 0
  "string2", // index 1
  "string3", // index 2  
}

That's easy for a language like python, where one can just do something like MyData[1] to get "string2" and MyData.index("string2") to get 1.

A few possible solutions would be to

  • write my own function to get the index by iterating over the array / slice
  • sort the array / slice and use a search function to return index (though this doesn't allow for an unsorted sequence, which is what I'd prefer)
  • maintain a map and an array that mirror each other, which is prone to errors.

Speaking of maps, can one access the key of a particular value? Then I could simply have a map like the following, and be able to get the string key when providing the int value.

var MyData = map[string]int {
  "string1": 0,
  "string2": 1,
  "string3": 2,
}

UPDATE: Before I accept my answer, I want to explain the problem more thoroughly, which I know must be fairly common. I basically have a set of strings that are constant (such as a list of countries) each with an associated integer value. In my database I simply store the integer to conserve space, since there are millions of entries. But when I display an entry from the database I need to display the string value for it to be readable to a user. A simple array will do for that. However, I also need to add entries to the database (such as a new person and their country of residence) and in this scenario need to translate from the country string which is entered in a form to that integer value. Again, this is just an example use case, but the goal remains the same. I need a table that can translate in both directions between a string value and an enumerated int value. The most obvious thing to do is to maintain an array (for the int to string translation) and a map (for the string to int translation). I'd prefer not to manually maintain both variables, since this is prone to errors. So my solution below is to maintain just a single array, and have the constructor method automatically build the map at runtime when the program is first run. This has the advantage of not needing to iterate over the entire array when I fetch the integer value based on the string (which was the other proposed solution).

3
This thread from a while back seems to be what you're looking for. stackoverflow.com/questions/8307478/…user185578
I figure I could actually go with the 3rd option of maintaining a map[string]int, and creating the mirrored []string array in runtime, so that I wouldn't be as prone to errors mirroring them manually, and it'd be more performant than iterating over the array to determine the index.onlinespending

3 Answers

4
votes

In both cases you should just use the built in range function.

for k, v := range MyData {
}


for i, v := range ThisArray {
}


for i, _ := range ThisArrayIndexOnly {
    value := ThisArrayIndexOnly[i]
}

You can build helper functions or whatever you like on top of this but range is fundamentally the mechanism available for accessing that data. If you want an "indexof" function it would be

for i, v := range ArrayPassedIntoFunction {
     if v == ValuePassedIntoFunction {
          return i
     }
}
return -1

To get the value, you of course would just do MyArray[i] including a bounds check or whatever. Note the pseudo code above is written in a style that indicates it's an array but virtually the same code will work for a map, I would just typically use the var name k instead of i.

3
votes

Assume you want getting index of word in the data of array

data := [...] {"one","two","three"}

or fixed length array

data := [3] {"one","two","three"}

create function

func indexOf(word string, data []string) (int) {
    for k, v := range data {
        if word == v {
            return k
        }
    }
    return -1
}

to get value from function above, to match the type, pass the array with array[:] like below

fmt.Println(indexOf("two", data[:]))
2
votes

Here's a solution that I mentioned earlier, which works well for static slices (which is my use case). Iterating over the slice every time I want the index of a value adds unnecessary delay, especially since my data is static during runtime. This just creates a struct which initializes the slice and creates the corresponding inverse map. And then I would use the GetKey and GetVal methods to get either the string 'key' by providing the int 'value', or get the int 'value' by providing the string 'key'. Perhaps there's already a way to get the key of a particular value of a map in Go.

type MyData struct {
    dataslice []string
    datamap map[string]int
}

func NewMyData() *MyData {
    m := new(MyData)
    m.dataslice= []string {
        "string1",
        "string2",
        "string3",
    }
    m.datamap = make(map[string]int)
    for x := range m.dataslice {
        m.datamap[m.dataslice[x]] = x
    }
    return m
}

func (m *MyData) GetKey(x int) string {
    return m.dataslice[x]
}

func (m *MyData) GetVal(x string) int {
    return m.datamap[x]
}