To initialize a map in a struct one should do the following:
someStruct.nestedMap = make(map[int8]int8)
But what should you do if you have a code structure like this:
type Base struct {
base map[int8]uint64
}
type Middle struct {
baseObjects map[int8]Base
}
type Top struct {
middleObjects map[int8]Middle
}
There we have a total of 3 structs where each has a struct as a key. How do you initialize this, and make it ready to go?
What you want is auctually a map with default value that is not zero value, but a value ready to use, in this case, a made map.
Before going to the solution, there auctually is an issue about go here: https://github.com/golang/go/issues/3117
For reasons, Go now does not support to assign to a field of a struct if the struct is stored in a map.
To solve the issue, you need to use a pointer to that struct, and store the pointer in the map instead. So your data structure needs to be changed like:
type Base struct {
base map[int8]uint64
}
type Middle struct {
baseObjects map[int8]*Base
}
type Top struct {
middleObjects map[int8]*Middle
}
And to the core logic: a defualt value map. Go does not support that with plain maps, but we can extend it, using methods to wrap it around.
func (t Top) Get(i int8) *Middle {
x, ok := t.middleObjects[i]
if !ok {
v := NewMiddle()
t.middleObjects[i] = v
return v
}
return x
}
func (m Middle) Get(i int8) *Base {
x, ok := m.baseObjects[i]
if !ok {
v := NewBase()
m.baseObjects[i] = v
return v
}
return x
}
When we are trying to get a value, we first check if it exists. If not, we return a new constructed one, and if it did exist, return the value.
Usage:
t.Get(8).Get(9).base[10] = 14
Playground example: https://play.golang.org/p/0JSN0yjRPif
Simply initialize the topmost struct Top
with an empty map as value for its middleObjects
property, calling any index on an empty map returns a zero value for the type the map holds.
package main
import (
"fmt"
)
type Base struct {
base map[int8]uint64
}
type Middle struct {
baseObjects map[int8]Base
}
type Top struct {
middleObjects map[int8]Middle
}
func main() {
top := Top{
middleObjects: make(map[int8]Middle),
}
// Outputs: "Top: {map[]}"
fmt.Printf("Top: %v
", top)
// Outputs: "Base element: 0"
fmt.Printf("Base element: %v
", top.middleObjects[5].baseObjects[3].base[0])
}
Run the example here
Edit:
You could also initialize the Top
struct with a couple of elements, in any case it depends on how many elements you require each map to have at initialization:
func main() {
top := Top{
middleObjects: map[int8]Middle{
0: Middle{
baseObjects: map[int8]Base{
0: Base{
base: map[int8]uint64{
0: 1234,
},
},
},
},
},
}
// After that you have to add one by one
top.middleObjects[5] = Middle{
baseObjects: map[int8]Base{
0: Base{
base: map[int8]uint64{
0: 123456,
},
},
},
}
// If there's a middleObject with that index
top.middleObjects[0].baseObjects[1] = Base{
base: map[int8]uint64{
0: 1111,
},
}
// Outputs: "Base element: 1234"
fmt.Printf("Base element: %v
", top.middleObjects[0].baseObjects[0].base[0])
// Outputs: "Base element: 123456"
fmt.Printf("Base element: %v
", top.middleObjects[5].baseObjects[0].base[0])
// Outputs: "Base element: 1111"
fmt.Printf("Base element: %v
", top.middleObjects[0].baseObjects[1].base[0])
top.middleObjects[0].baseObjects[0].base[0] = 2222
// Outputs: "Base element: 2222"
fmt.Printf("Base element: %v
", top.middleObjects[0].baseObjects[0].base[0])
}
Run example here