Initializing a Go map in a single statement
Solution 1
Yes, you can create a map with a single statement (called a composite literal in the spec):
var keys = map[int]string{
1: "aa",
2: "ab",
3: "ac",
4: "ba",
5: "bb",
6: "bc",
7: "ca",
8: "cb",
9: "cc",
}
Or, if you are inside of a function, you can use a short variable declaration:
keys := map[int]string{
1: "aa",
2: "ab",
3: "ac",
4: "ba",
5: "bb",
6: "bc",
7: "ca",
8: "cb",
9: "cc",
}
Solution 2
When there is logic between keys and values, you may also use a loop to initialize the map. "Put" the logic into the loop body. This may be significantly shorter than using a composite literal enumerating all key-value pairs, especially if the number of key-value pairs is big.
Your example can be implemented with this:
m := map[int]string{}
for i := 0; i < 9; i++ {
m[i+1] = string("abc"[i/3]) + string("abc"[i%3])
}
fmt.Println(m)
Output (try it on the Go Playground):
map[5:bb 8:cb 4:ba 2:ab 3:ac 6:bc 7:ca 9:cc 1:aa]
A variant of this solution (using a different logic implementation):
m := map[int]string{}
for i := 0; i < 9; i++ {
m[i+1] = "abc"[i/3:i/3+1] + "abc"[i%3:i%3+1]
}
fmt.Println(m)
Output is the "same". Try this variant on the Go Playground.
And even more solutions, now posting only the loop body (Playground links: another #1, another #2):
// Another #1:
m[i+1] = fmt.Sprintf("%c%c", "abc"[i/3], "abc"[i%3])
// Another #2:
m[i+1] = fmt.Sprintf("%c%c", 'a'+i/3, 'a'+i%3)
A different approach might use 2 loops (embedded) which generate the value, and calculates the key from the value:
for i := 'a'; i <= 'c'; i++ {
for j := 'a'; j <= 'c'; j++ {
m[int((i-'a')*3+j-'a'+1)] = string(i) + string(j)
}
}
Try this on the Go Playground.
If the number of values is not big, another viable approach can be to enumerate all the elements in one string
value, and use subslicing (which is efficient as no new backing arrays will be created, the backing array of the strings is shared):
const s = "aaabacbabbbccacbcc"
m := map[int]string{}
for i := 0; i < 9; i++ {
m[i+1] = s[i*2 : i*2+2]
}
fmt.Println(m)
Output (try this on the Go Playground):
map[9:cc 1:aa 2:ab 5:bb 8:cb 3:ac 4:ba 6:bc 7:ca]
Also note that if the key is of type int
and the set of keys is (more or less) contiguous, often it is more efficient (both memory and performance-wise) to use a slice instead:
m := make([]string, 10)
for i := 0; i < 9; i++ {
m[i+1] = fmt.Sprintf("%c%c", 'a'+i/3, 'a'+i%3)
}
fmt.Printf("%q\n", m)
m2 := []string{"", "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"}
fmt.Printf("%q\n", m2)
m3 := []string{1: "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"}
fmt.Printf("%q\n", m3)
Output (try it on the Go Playground):
["" "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc"]
["" "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc"]
["" "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc"]
As you can see in the third example m3
, you can use optional indices in the composite literal to specify the index of the value following. More about this here: Keyed items in golang array initialization
Solution 3
My preferred approach is a composite literal in a short variable declaration. In some cases a function might help to reduce clutter.
package main
import (
"fmt"
)
// initMap initializes a map with an integer key starting at 1
func initMap(sa []string) map[int]string {
m := make(map[int]string, len(sa))
for k, v := range sa {
m[k+1] = v // add 1 to k as it is starting at base 0
}
return m
}
// main is the entry point of any go application
func main() {
// My preferred approach is a composite literal in a short variable declaration
keys := map[int]string{1: "aa", 2: "ab", 3: "ac", 4: "ba", 5: "bb", 6: "bc", 7: "ca", 8: "cb", 9: "cc"}
fmt.Println(keys)
// Using a function to initialize the map might help to avoid clutter
keys2 := initMap([]string{"aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"})
fmt.Println(keys2)
}
See it in action at https://play.golang.org/p/Rrb9ChBkXW
![sensorario](https://i.stack.imgur.com/wJ6T0.png?s=256&g=1)
Comments
-
sensorario almost 2 years
This is my code:
var keys map[int]string keys = make(map[int]string) keys[1] = "aa" keys[2] = "ab" keys[3] = "ac" keys[4] = "ba" keys[5] = "bb" keys[6] = "bc" keys[7] = "ca" keys[8] = "cb" keys[9] = "cc"
Can I do the same thing in one statement and/or in one line?
-
subrat71 over 7 yearsYour
initMap
function should supply an initial capacity for the map as a second argument tomake
:len(sa)
. -
Peter Gloor over 7 yearsThe capacity is important for slices, but maps work different and dont have a capacity. You are right, in case of large maps you can and should provide an optional size parameter in the make statement. In case of small maps, like in this example, you probably wont recognize any measurable performance gain.
-
subrat71 over 7 yearsFine, call it "initial space," as the specification does. The point is that it avoids the need for subsequent allocation and reorganization of the map. It's the "capacity for avoiding table growth" given its current allocation.