Use different structs as a value in map golang - go

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

Related

Go - How to create and initialize a struct instance based on conditions when there are multiple custom type structs with the same attributes?

I'm still new to Go and am facing a situation that needs some help here.
Assume there are two types of structs with the SAME attribute list:
type PersonA struct {
[long list of attributes...]
}
type PersonB struct {
[same long list of attributes...]
}
And I would like to create an instance and initialize based on some conditions like below:
var smartPerson [type]
func smartAction(personType string) {
switch personType
case "A":
smartPerson = PersonA{long list initialization}
case "B":
smartPerson = PersonB{same long list initialization}
}
fmt.Println(smartPerson)
There are two problems here:
First - 'smartPerson' needs to be the instance type ascertained in the switch statement.
Second - the 'long list initialization' is the same for all conditions, so better to avoid repetition.
Is it possible to do this in Go?
You can, do something like this by embedding a common struct in both PersonA and PersonB.
For example (playgound link):
package main
import "fmt"
type commonPerson struct {
A string
B string
C string
}
type PersonA struct {
commonPerson
}
func (p PersonA) String() string {
return fmt.Sprintf("A: %s, %s, %s", p.A, p.B, p.C)
}
// This function is just here so that PersonA implements personInterface
func (p PersonA) personMarker() {}
type PersonB struct {
commonPerson
}
func (p PersonB) String() string {
return fmt.Sprintf("B: %s, %s, %s", p.A, p.B, p.C)
}
// This function is just here so that PersonB implements personInterface
func (p PersonB) personMarker() {}
type personInterface interface {
personMarker()
}
var smartPerson personInterface
func smartAction(personType string) {
common := commonPerson{
A: "foo",
B: "bar",
C: "Hello World",
}
switch personType {
case "A":
smartPerson = PersonA{commonPerson: common}
case "B":
smartPerson = PersonB{commonPerson: common}
}
}
func main() {
smartAction("A")
fmt.Println(smartPerson)
smartAction("B")
fmt.Println(smartPerson)
}
Outputs:
A: foo, bar, Hello World
B: foo, bar, Hello World

Is there a nice way to get length of slice elem in map regardless of its concrete type?

For example, I have two maps, namely map[string][]structAand map[string][]int.
I want to iterate the map and print out length of every slice, can I implement this in a single function?
I tried to define an interface and implement it in two kinds of slices, but the compiler just cannot do the type cast.
type Container interface{
Len() int
}
type Slice1 []int
func (s Slice1) Len() int {
return len(s)
}
type Slice2 []StructA
func (s Slice2) Len() int {
return len(s)
}
func iterate(input map[string]Container) {
for key, value := range input {
log.Printf("key:%s, lenght:%d", key, value.Len())
}
}
func main() {
// cannot do the type cast
iterate(map[string]Slice1{})
iterate(map[string]Slice2{})
}
"can I implement this in a single function?"
Yes, but the way you called the function should be changed. Convert your maps to map[string]Container and then call the iterate
func main() {
// cannot do the type cast
map1 := map[string][]int{
`key`: []int{1,2,3},
}
map1Container := make(map[string]Container)
for key, value := range map1 {
map1Container[key] = Slice1(value)
}
map2 := map[string][]StructA{
`key1`: []StructA{{}, {}},
}
map2Container := make(map[string]Container)
for key, value := range map2 {
map2Container[key] = Slice2(value)
}
iterate(map1Container)
iterate(map2Container)
}
Output:
2021/07/06 12:15:25 key:key, lenght:3
2021/07/06 12:15:25 key:key1, lenght:2
iterate function expected map[string]Container type parameter. So you can initiate that with Container type and inside the map with different keys, you can include different Container's implementations.
func main() {
// cannot do the type cast
iterate(map[string]Container{
`ke1`: Slice1([]int{
1, 2, 3,
}),
`ke2`: Slice2([]StructA{
{},
{},
}),
})
}
Output:
2021/07/06 11:57:55 key:ke1, lenght:3
2021/07/06 11:57:55 key:ke2, lenght:2

How do i recover the struct type after make it as Interface in Golang

I want to merge some similar func code to one func, but every old func is use different type of struct, so i intend to create the model by different string of type.
SO i do something like this:
type A struct {
filed string
}
type B struct {
filed string
}
and still C, D, E, F here...(every struct has its own method different with others)
and i want create those type in one place:
create(typeName string) interface {
switch typeName {
case A:
return &A{}
case B:
return &B{}
....(more case than 10 times)
}
}
Then i use the create() here:
model := create("A")
now, model is type of interface, and no A`s fileds, how would simply to recover the type of model to A
Here is a sample of how you can use type assertion to convert interfaces to underlying structs
Here e is of the struct type and hence you can access any of its fields or struct methods.
package main
import (
"fmt"
)
type A struct {
AF int
}
type B struct {
BF string
}
func main() {
ds := []interface{}{
A{1},
B{"foo"},
}
for _, d := range ds {
switch e := d.(type) {
case A:
fmt.Println(e.AF)
case B:
fmt.Println(e.BF)
}
}
}

Convert struct to base class

In my understanding, there is no inherent and polymorphism in golang, so how to get the sub-struct from a given struct? For example, struct A has several attributes:
type A struct {
a int
b string
c *float64
d []byte
e map[string]interface{}
}
I want to delete the e:
type B struct {
a int
b string
c *float64
d []byte
}
So is there any way to convert A to B except copy variables one by one?
You can do that with embedded fields. See Struct Types in the Go Language Specification, e.g.:
Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField).
With an embedded field, you can copy the fields in one struct to the struct in which it is an embedded field with a simple assignment to the field.
Here's a complete, runnable example:
package main
import (
"encoding/json"
"fmt"
)
type Shared struct {
Id int
Name string
}
type A struct {
Count int
Shared
}
type B struct {
Color string
Shared
}
func main() {
shared := Shared{
Id: 1,
Name: "john",
}
a := A{
Count: 5,
Shared: shared,
}
b := B{
Color: "green",
}
b.Shared = a.Shared
c := map[string]interface{}{
`a`: a,
`b`: b,
}
msg, err := json.Marshal(c)
if err != nil {
fmt.Printf("Marshal error: %s\n", err)
}
fmt.Println(string(msg))
}
Prints:
{"a":{"Count":5,"Id":1,"Name":"john"},"b":{"Color":"green","Id":1,"Name":"john"}}

Function that takes different type of struct

I wonder how the following functions lengthAA and lengthBB can be simplified to just one function. Note in those two functions it just compute the length of the array, it is just an example and can be more complicated than that. Ideally, I want only one function that serve the same purpose (in this case, len) but can take different struct as variable.
type A struct {
id string
}
type AA struct {
ids []A
}
type B struct {
id string
value bool
}
type BB struct {
ids []B
}
func lengthAA(aa AA) int {
return len(aa)
}
func lengthBB(bb BB) int {
return len(bb)
}
The Go way of doing this would be for both AA and BB to implement a common method. length would then accept an interface that contains the same function signature. Example:
package main
import (
"fmt"
)
type Lengther interface {
Length() int
}
type A struct {
id string
}
type AA struct {
ids []A
}
func (a *AA) Length() int {
return len(a.ids)
}
type B struct {
id string
value bool
}
type BB struct {
ids []B
}
func (b *BB) Length() int {
return len(b.ids)
}
func length(l Lengther) int {
return l.Length()
}
func main() {
aa := &AA{
ids: make([]A, 10),
}
bb := &BB{
ids: make([]B, 34),
}
fmt.Println(length(aa))
fmt.Println(length(bb))
}
https://play.golang.org/p/DdxP5lFcZi
1- Using two separate receiver methods length() like this working sample code (This is idiomatic Go):
package main
import "fmt"
func (v *AA) length() int {
return len(v.ids)
}
func (v *BB) length() int {
return len(v.ids)
}
func main() {
aa := AA{[]A{A{"id"}, A{"id2"}}}
fmt.Println(aa.length()) // 2
bb := BB{[]B{B{"id", true}, B{"id2", true}}}
fmt.Println(bb.length()) // 2
}
type A struct {
id string
}
type AA struct {
ids []A
}
type B struct {
id string
value bool
}
type BB struct {
ids []B
}
2- Using one length(aa interface{}) function, like this working sample code (in some use cases this is useful):
package main
import "fmt"
func length(aa interface{}) int {
switch v := aa.(type) {
case AA:
return len(v.ids)
case BB:
return len(v.ids)
}
return -1
}
func main() {
aa := AA{[]A{A{"id"}, A{"id2"}}}
fmt.Println(length(aa)) // 2
bb := BB{[]B{B{"id", true}, B{"id2", true}}}
fmt.Println(length(bb)) // 2
}
type A struct {
id string
}
type AA struct {
ids []A
}
type B struct {
id string
value bool
}
type BB struct {
ids []B
}
3- Using reflect and one length(v interface{}) function, like this working sample code (in some use cases this is useful):
package main
import "fmt"
import "reflect"
func length(v interface{}) int {
return reflect.ValueOf(v).FieldByName("ids").Len()
}
func main() {
aa := AA{[]A{A{"id"}, A{"id2"}}}
fmt.Println(length(aa)) // 2
bb := BB{[]B{B{"id", true}, B{"id2", true}}}
fmt.Println(length(bb)) // 2
}
type A struct {
id string
}
type AA struct {
ids []A
}
type B struct {
id string
value bool
}
type BB struct {
ids []B
}
output:
2
2
This code wouldn't actually compile since len(aa) would be passing a struct to len which would fail. But I think I get what you're trying to do, and the best way to do it is with the nearest thing Go has to inheritance, which is really just struct embedding.
By making an embeddable struct, and having each of your structs that have that similar feature embed it, you can reduce the line count of the code, though "simplifying" may be a bit of a stretch.
type HasIDs struct {
Ids []string
}
type AA struct {
HasIDs
OtherValues []int
}
type BB struct {
HasIDs
OtherValues []byte
}
At this point both structs AA and BB have the value Ids []string. Now you can give the HasIDs struct a method that both structs should be able to call.
func (hasIds HasIDs) length() {
return len(hasIds.Ids)
}
And call the method on either struct that embeds the struct with the method.
a1 := AA{}
aLength := a1.length()
Here is a working code sample: https://play.golang.org/p/ys_CN_L_cr

Resources