The problem:
I've got a map of structs in another struct and I'd like to initialize the nested map of structs, but apparently that is not possible.
Code:
type Exporter struct {
TopicsByName map[string]Topic
}
type Topic struct {
Name string
Partitions map[int32]Partition
}
type Partition struct {
PartitionID int32
HighWaterMark int64
}
// Eventually I want to do something like:
e := Exporter{ TopicsByName: make(map[string]Topic) }
for _, topicName := range topicNames {
// This does not work because "cannot assign to struct field e.TopicsByName[topicName].Partitions in map"
e.TopicsByName[topicName].Partitions = make(map[int32]Partition)
}
// I wanted to initialize all these maps so that I can do
e.TopicsByName[x.TopicName].Partitions[x.PartitionID] = Partition{...}
I don't understand why I can not initialize the nested struct map above. Is it so bad to nest maps with struct as values? How can I fix this?
It is not possible to assign to a field in a map value. The fix is to
assign a struct value to the map value:
for _, topicName := range []string{"a"} {
e.TopicsByName[topicName] = Topic{Partitions: make(map[int32]Partition)}
}
You can initialize it like you'd expect:
e := Exporter{
TopicsByName: map[string]Topic{
"my-topic": Topic{
Name: "my-topic",
Partitions: map[int32]Partition{
int32(1): Partition{
PartitionID: int32(1),
HighWaterMark: int64(199),
},
},
},
},
}
This isn't directly answering the question, since you wanted to iterate over a list of topics, but if this is used in kafka testing, I highly recommend the more verbose/literal format above.
https://play.golang.org/p/zm3A7CIvbyu
If you've known an initialed value. Why don't do it like
val := `
{
"topics_by_name": {
"name1": {
"name":"topicname1",
"partions": {
1: {
"partion_id":1,
"high_water_mark":12,
},
2: {}
}
},
"name2": {}
}
}
`
func main() {
var m map[string]interface{}
// if m has be well designed, use your designed map is better
// init
json.Unmarshal(val, &m)
}
Related
There is the following field on dynamo
{
"config": {
"BASE_AUTH_URL_KEY": "https://auth.blab.bob.com",
"BASE_URL": "https://api.dummy.data.com",
"CONN_TIME_OUT_SECONDS": "300000",
"READ_TIME_OUT_SECONDS": "300000"
},
"id": "myConfig"
}
and getting the element with dynamodbattribute
import(
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute")
result, err := svc.GetItem(&dynamodb.GetItemInput{
TableName: aws.String(tableName),
Key: map[string]*dynamodb.AttributeValue{
"id": {
S: aws.String(configId),
},
},
})
this code its working but when i try to retrieve the object its rendered like this
map[config:{
M: {
BASE_AUTH_URL_KEY: {
S: "https://auth.blab.bob.com"
},
CONN_TIME_OUT_SECONDS: {
S: "300000"
},
READ_TIME_OUT_SECONDS: {
S: "300000"
},
BASE_URL: {
S: "https://api.dummy.data.com"
}
}
} id:{
S: "myConfig"
}]
for that reason when i try to unmarshal my object the object unmarshalled returns as {}
type Config struct {
id string
baseAuthUrlKey string
baseUrl string
connectTimeOutSecs string
readTimeOutSecs string
}
item := Config{}
err = dynamodbattribute.UnmarshalMap(result.Item, &item)
how can i assign the value return from the GetItem that seems to be a map to my struct ?
The root of the issue is that your Config struct is incorrectly structured.
I recommend using json-to-go when converting JSON to Go structs; this tool will help you catch issues like this in the future.
Once you get your struct constructed correctly, you'll also notice that your struct fields are not capitalized, meaning they will not be exported (i.e. able to be used by other packages), which is another reason that your UnmarshalMap code will not return the result you are expecting.
Here is a good answer on struct field visibility and its importance, briefly summarized above.
Below is a corrected version of your struct that, combined with your UnmarshalMap code, will correctly allow you to print your item and not receive a {} which is no fun.
type Item struct {
Config struct {
BaseAuthUrlKey string `json:"BASE_AUTH_URL_KEY"`
BaseUrl string `json:"BASE_URL"`
ConnTimeoutSeconds string `json:"CONN_TIME_OUT_SECONDS"`
ReadTimeoutSeconds string `json:"READ_TIME_OUT_SECONDS"`
} `json:"config"`
ID string `json:"id"`
}
I have a struct that contains a slice of a struct and I am trying to retrieve the data from the inner struct. Here is an example of the struct:
type Data struct {
Quotes []struct {
Direct bool `json:"Direct"`
Legs struct {
ID int `json:"Id"`
} `json:"Legs"`
}
}
From the above code I would like to retrieve the value in ID. Here is what I have tried already:
for _, v := range Data.Quotes.Legs {
fmt.Println(v.ID)
}
But I get the following error:
Has no field or method Legs
This works if I just want the value in Direct:
for _, v := range Data.Quotes {
fmt.Println(v.Direct)
}
Does anyone have suggestions on how to do this?
Data.Quotes.Legs is not an array. Data.Quotes is:
var data Data
...
for _, v := range data.Quotes {
fmt.Println(v.Legs.ID)
}
Is there any way to create a map into several structs, and then use It?
I have several different structs that implement the same interface and matching input types for each struct.
I want to read data from the different inputs into the structs - without knowing the input type at the compilation time.
type myInput struct {
InputType string
data []bytes
}
// Will get as an input after compeleation
inputs := []myInput{
myInput{InputType: "a", data: []bytes{0x01, 0x02, 0x03}},
myInput{InputType: "b", data: []bytes{0x01, 0x02}},
}
type StructA struct {
A uint16
B uint32
}
func (a StructA) name () {
fmt.Printf("name is: %d %d", a.A, a.B)
}
type StructB struct {
C uint32
}
func (b StructB) name () {
fmt.Printf("name is: %d", b.C)
}
AorB map[string]<???> {
"a": StructA,
"b": StructB,
}
At this point, I don't know what to do. I need to take the correct struct by the input type and initialize the struct using binary.Read.
for _, myInput := range (inputs) {
// ???? :(
myStruct := AtoB[myInput.InputType]{}
reader :=bytes.NewReader(input1)
err := binary.Read(reader, binary.BigEndian, &myStruct)
fmt.Printf(myStruct.name())
}
Thanks!
Define a interface
type Bin interface {
name() string
set([]byte) // maybe returning error
}
You'll be handling Bins only.
type StructA struct { /* your code so far */ }
type StructB struct { /* your code so far */ }
func (a *StructA) set(b []byte) {
a.A = b[0]<<8 + b[1] // get that right, too lazy to code this for you
a.B = b[2]<<24 + b[3]<<16 + ...
}
// same for StructB
So your StructA/B are Bins now.
func makeBin(in myInput) Bin {
var bin Bin
if in.InputType == "a" {
bin = &StructA{}
} else {
bin = &StructB{}
}
bin.set(in.data) // error handling?
return bin
}
If you have more than two types: Use a switch instead if an if or make a tiny type registry (reflect).
First you define an interface for the commonly used func name:
type Namer interface {
name()
}
Then you can create a map to that interface and insert structs:
AorB := map[string] Namer {
"a": StructA{
A: 42,
B: 28,
},
"b": StructB{
C: 12,
},
}
Now you can access all entries:
for _, n := range AorB {
n.name()
}
You can use interface for the same
AorB := map[string]interface{}{
"a": StructA{},
"b": StructB{},
}
When you retrieve value back you can assert into A for type A and B for type B
I'm new working on golang, In this case I have a map [string] which has a struct.
At this point everything is works.
But I would like to have a map[string] in which I can access at the same time to another map[string] which has it self struct.
This is the code in which I'm trying to work.
type myStruct struct{
atrib1 string
atrib2 string
}
var apiRequest map[string] map[string]myStruct
I would like acces to something like this:
func main() {
apiRequest = make(map[string] map[string]myStruct)
apiKeyTypeRequest["Key"]["MyFirstOption"].atrib1 = "first Value first op"
apiKeyTypeRequest["Key"]["MyFirstOption"].atrib2 = "second Value first op"
apiKeyTypeRequest["Key"]["MysecondtOption"].atrib1 = "first Value second op"
}
An alternative to using a map inside a map, is to have a single map[mapKey] where mapKey is a struct:
type mapKey struct {
Key string
Option string
}
The benefits is that you only have to do a single lookup when searching for a myStruct, and you only have to create a single map.
The downside is in case you need to be able to get that options map[string]myStruct map, since it does not exist. Also, you cannot check if a certain key exists or not, because keys and options exists in pairs.
Working example:
package main
import "fmt"
type myStruct struct {
atrib1 string
atrib2 string
}
type mapKey struct {
Key string
Option string
}
func main() {
apiKeyTypeRequest := make(map[mapKey]myStruct)
apiKeyTypeRequest[mapKey{"Key", "MyFirstOption"}] = myStruct{"first Value first op", "second Value first op"}
apiKeyTypeRequest[mapKey{"Key", "MysecondtOption"}] = myStruct{atrib1: "first Value second op"}
fmt.Printf("%+v\n", apiKeyTypeRequest)
}
Playground: http://play.golang.org/p/tGd7ja7QI2
To expand upon previous answers, each map must be declared and instantiated (as well as the struct at the end of the map), that means you'll need to instantiate the "outer" map
mapOfMaps := make(map[string]map[string]myStruct)
as well as the "inner" map(s) for each key you have.
mapOfMaps[key] = make(map[string]myStruct)
The obvious issue you run into here is how do you dynamically check to see if the mapOfMaps[key] has already been instantiated? You do this using the following syntax:
if _, ok := mapOfMaps[key]; !ok {
mapOfMaps[key] = make(map[string]myStruct)
}
This syntax checks to see if mapOfMaps already has an instantiated value for that key, and if not instantiates it. Similarly, you can use the same syntax to instantiate the structs you are using by checking to see of mapOfMaps[key][key2] has been instantiated yet.
if _, ok := mapOfMaps[key][key2]; !ok {
mapOfMaps[key][key2] = new(myStruct)
}
Now you can access the struct through the call to 2 keys and an attribute of the struct
fmt.Println(mapOfMaps[key][key2].atrib1)
As #FUZxxl has said, you need to initialize the sub-map for each outer map, but there is a shorthand syntax for it:
type myStruct struct {
atrib1 string
atrib2 string
}
func main() {
var myMap = map[string]map[string]myStruct{
"foo": {
"bar": {attrib1: "a", attrib2: "b"},
"baz": {"c", "d"}, //or by order...
},
"bar": {
"gaz": {"e", "f"},
"faz": {"g", "h"},
},
}
fmt.Println(myMap["foo"]["bar"].atrib1)
fmt.Println(myMap["bar"]["gaz"].atrib2)
}
I would have expected this code to work:
package main
type Item struct {
Key string
Value string
}
type Blah struct {
Values []Item
}
func main() {
var list = [...]Item {
Item {
Key : "Hello1",
Value : "World1",
},
Item {
Key : "Hello1",
Value : "World1",
},
}
_ = Blah {
Values : &list,
}
}
I thought this would be the correct way of doing this; Values is a slice, list is an array. &list should be a slice, which is assignable to Item[], right?
...but instead, it errors with the message:
cannot use &list (type *[2]Item) as type []Item in assignment
In C, you'd write:
struct Item {
char *key;
char *value;
};
struct Blah {
struct Item *values;
};
How do you do that in Go?
I saw this question:
Using a pointer to array
...but either the answers are for a previous version of Go, or they're just plain wrong. :/
A slice is not simply a pointer to an array, it has an internal representation which contains its length and capacity.
If you want to get a slice from list you can do:
_ = Blah {
Values : list[:],
}
Go is, fortunately, not so verbose as it might seem from the OP. This works:
package main
type Item struct {
Key, Value string
}
type Blah struct {
Values []Item
}
func main() {
list := []Item{
{"Hello1", "World1"},
{"Hello2", "World2"},
}
_ = Blah{list[:]}
}
(Also here)
PS: Let me suggest to not write C in Go.
When you are starting out with Go ignore arrays completely and just use slices is my advice. Arrays are rarely used and cause Go beginners a lot of trouble. If you have a slice then you don't need a pointer to it since it is a reference type.
Here is your example with a slice and no pointers which is much more idiomatic.
package main
type Item struct {
Key string
Value string
}
type Blah struct {
Values []Item
}
func main() {
var list = []Item{
Item{
Key: "Hello1",
Value: "World1",
},
Item{
Key: "Hello1",
Value: "World1",
},
}
_ = Blah{
Values: list,
}
}