go: map of pointers or bools as map of interface - go

Suppose I have maps of different types, among them of bools and of pointers and want to pass them to some function in single way:
type Blah struct {
name string
}
var mapBlah map[string]*Blah = make(map[string]*Blah)
var mapBool map[string]bool = make(map[string]bool)
func joinKeys(m map[string]interface{}) string {
// returns strings.Join(keys of map)
}
I seemingly can't pass these maps into this function (example). How can this be done properly, or
why it can't be done if I'm missing something, please?

When you already know the types, I think switching over types will be beneficial like follows.
package main
import (
"fmt"
"strings"
)
type Blah struct {
name string
}
var mapBlah map[string]*Blah = make(map[string]*Blah)
var mapBool map[string]bool = make(map[string]bool)
func joinKeys(m interface{}) string {
var a []string
switch v := m.(type) {
case map[string]*Blah:
for k, _ := range v {
a = append(a, k)
}
case map[string]bool:
for k, _ := range v {
a = append(a, k)
}
default:
}
return strings.Join(a, ",")
}
func main() {
mapBlah["1B"] = &Blah{name: "first"}
mapBlah["2B"] = &Blah{name: "second"}
fmt.Println(joinKeys(mapBlah))
mapBool["1Bool"] = true
fmt.Println(joinKeys(mapBool))
}
For more dynamic way with some performance tradeoffs reflection is way to go.

Related

How to ensure read-only variables and maps in golang

I want my programs have access to global read-only regex and maps. Here's what i thought of :
var myRegex *regexp.Regexp
var myMap map[string]string
func init() {
myRegex = regexp.MustCompile("blah")
myMap = map[string]string{"blah": "blah"}
}
or i can do
type myStruct struct {
// already have bunch of other struct fields
myRegex *regexp.Regexp
myMap map[string]string
}
func Initialize() *myStruct {
m := myStruct {
// bunch of other stuff
myRegex: regexp.MustCompile("blah")
myMap: map[string]string{"blah": "blah"}
}
But how i can ensure that these variables won't be writable by any other piece of my software ?
As long as you're dealing with pointers ( maps are pointers anyway ), you' ll never be able to ensure that your map or regex is ReadOnly.
( Ok, unless you copy the value with the function each time and return a new pointer... But i'm not sure that's what you want to achieve :)
If i take your example, and add a simple main code :
package main
import (
"fmt"
"regexp"
)
var myRegex *regexp.Regexp = regexp.MustCompile("blah")
var myMap map[string]string
func init() {
myRegex = regexp.MustCompile("blah")
myMap = map[string]string{"blah": "blah"}
}
type myStruct struct {
// already have bunch of other struct fields
myRegex *regexp.Regexp
myMap map[string]string
}
func Initialize() myStruct {
return myStruct{
// bunch of other stuff
myRegex: regexp.MustCompile("blah"),
myMap: map[string]string{"blah": "blah"},
}
}
func getMap() map[string]string {
return myMap
}
func main() {
fmt.Println(myMap)
myMap["blah2"] = "blah2"
fmt.Println(myMap)
fmt.Println(getMap())
m := Initialize()
fmt.Println(m.myMap)
m.myMap["test"] = "test"
fmt.Println(m.myMap)
}
you see that i can modify the maps:
❯ ./main
map[blah:blah]
map[blah:blah blah2:blah2]
map[blah:blah blah2:blah2]
map[blah:blah]
map[blah:blah test:test]
Regex would be exactly the same.
If you really want to ensure that your regex and maps will never be updated by mistake by another piece of code, there's couple of solutions; most of them consisting of moving your read-only variables in there own package and never give direct access to them. Something like this for instance
package mapreadonly
type ReadOnlyMap struct {
m map[string]string
}
func (elem ReadOnlyMap) Get(key string) (string, bool) {
value, ok := elem.m[key]
return value, ok
}
var Map1 ReadOnlyMap = ReadOnlyMap{
m: map[string]string{
"blah": "blah",
},
}
and then import this package in your other files that requires it.
But as said, your question is lacking a bit of context to ensure that the answer is what your expect.

Iterate fields of different structures in one array

I want to iterate fields of different structures in one array
i try to do it like this
package main
import "fmt"
func main() {
m := msh{"hshHeader"}
p := pid{"pidHeader"}
var i []EmptyInterface
i = append(i, m)
i = append(i, p)
for _, emptyInterface := range i {
fmt.Println(emptyInterface.header)
}
}
type msh struct {
header string
}
type pid struct {
header string
}
type EmptyInterface interface {
}
As expected, on line 15 I get the message "Unresolved reference 'header'".
Do I understand correctly that in Go I should do this using methods
package main
import "fmt"
func main() {
m := msh{"hshHeader"}
p := pid{"pidHeader"}
var i []EmptyInterface
i = append(i, m)
i = append(i, p)
for _, NotEmptyInterface := range i {
fmt.Println(NotEmptyInterface.PrintHeader())
}
}
type msh struct {
header string
}
func (m msh) PrintHeader() string {
return m.header
}
type pid struct {
header string
}
func (p pid) PrintHeader() string {
return p.header
}
type EmptyInterface interface {
PrintHeader() string
}
I don’t know what else to add so that the bot on the site does not throw an error "It looks like your post is mostly code; please add some more details.", I can suggest reading again.

Avoid duplicating identical code for string map and slice

While writing two methods (one for a slice and one for a string map) I realized the implementation for both methods is identical and the only thing that changes is the prototype of the functions.
I'm trying to avoid repeating it and originally I thought of the following (see the FIXME part):
package main
import (
"fmt"
"strings"
)
type SomethingList []*Something
type SomethingMap map[string]*Something
type Something struct {
ID string
Type int
}
func (sl SomethingList) GetIDsString() string {
return getIDsString(sl)
}
func (sm SomethingMap) GetIDsString() string {
return getIDsString(sm)
}
func getIDsString(elements interface{}) string {
var ids []string
// FIXME: Yes, I know I can't iterate over an interface
for element = range elements {
ids = append(ids, element.ID)
}
return strings.Join(ids, ",")
}
func main() {
s1 := Something{ID: "ABC", Type: 1}
s2 := Something{ID: "DEF", Type: 1}
sl := SomethingList{&s1, &s2}
sm := SomethingMap{s1.ID: &s1, s2.ID: &s2}
fmt.Println(sl.GetIDsString())
fmt.Println(sm.GetIDsString())
}
The important part is the function getIDsString which basically takes the ID field of the struct and concatenates it's content across all the members of the slice or map.
I realize now after reading a bit about how interfaces work (yes, I'm quite a newbie in Go as is probably obvious already :-)) that this is not going to work, as Go is statically typed and I can't simply change the types on runtime. Also the interfaces don't allow me to iterate. I've been able to get close using a loop that iterates using the Len() method on the reflect.ValueOf and Index() to access each element. But Index() doesn't work on the string map.
What would be the most idiomatic way of solving this without duplicating much code?
Thanks!
In general repetition of small part of the code in golang is quite common. But in case you have a large amount of duplicate code, you can have that logic in one structure, and ad hoc transform the second structure to the first one to invoke that logic:
package main
import (
"fmt"
"strings"
)
type (
SomethingList []*Something
SomethingMap map[string]*Something
Something struct {
ID string
Type int
}
)
func (sl SomethingList) GetIDsString() string {
ids := make([]string, len(sl))
for i := range sl {
ids[i] = sl[i].ID
}
return strings.Join(ids, ",")
}
func (sm SomethingMap) GetIDsString() string {
l := make(SomethingList, len(sm))
i := 0
for key := range sm {
l[i] = sm[key]
i++
}
return l.GetIDsString()
}
func main() {
s1 := Something{ID: "ABC", Type: 1}
s2 := Something{ID: "DEF", Type: 1}
sl := SomethingList{&s1, &s2}
sm := SomethingMap{s1.ID: &s1, s2.ID: &s2}
fmt.Println(sl.GetIDsString())
fmt.Println(sm.GetIDsString())
}
Alternatively you could decouple the creation of IDsString from the structure itself in a following way.
package main
import (
"fmt"
"strings"
)
type (
SomethingList []*Something
SomethingMap map[string]*Something
Something struct {
ID string
Type int
}
somethingIterator interface {
ForEach(func(value Something))
}
)
func (sl SomethingList) ForEach(f func(value Something)) {
for i := range sl {
f(*sl[i])
}
}
func (sm SomethingMap) ForEach(f func(value Something)) {
for key := range sm {
f(*sm[key])
}
}
func GetIDsString(iterator somethingIterator) string {
var ids []string
iterator.ForEach(func(value Something) {
// Some sophisticated logic is here.
ids = append(ids, value.ID)
})
return strings.Join(ids, ",")
}
func main() {
s1 := Something{ID: "ABC", Type: 1}
s2 := Something{ID: "DEF", Type: 1}
sl := SomethingList{&s1, &s2}
sm := SomethingMap{s1.ID: &s1, s2.ID: &s2}
fmt.Println(GetIDsString(sl))
fmt.Println(GetIDsString(sm))
}
Second approach allows to avoid extra intermediate structure creation which could be beneficial for big list/map.

How to manipulate a map field in a structure with reflect package?

We have a structure like:
type S struct {
M map[string]bool
}
And how can we implement a function like:
// this function modify field by name to a new map
func Modify(s *S, name string, val map[string]bool){
...
}
func Test() {
s := S{
M: map[string]bool{"Hello":true},
}
m := map[string]bool{"World":false}
Modify(&s, "M", m)
}
The reflect package support SetInt/SetString/etc, but none SetMap.
Any way to solve this problem?
Use reflect.Set()
func Modify(s *S, name string, val interface{}) {
rv := reflect.ValueOf(val)
if !rv.IsValid() {
rv = reflect.Zero(reflect.ValueOf(s).Elem().FieldByName(name).Type())
}
reflect.ValueOf(s).Elem().FieldByName(name).Set(rv)
}
Playground

Why the map doesn't return its value?

I have a function which simply returns the value of a map. However for some reasons it doesn't do that.
Any idea why ? I've pasted here the code. You may also play it using
Try it!
package main
import "fmt"
func main() {
a := CountryCode("Australia")
fmt.Println(a)
}
func CountryCode(s string) string {
m := make(map[string]string)
// [snip]
m["AU"] = "Australia"
// [snip]
return m[s]
}
func CodeByCountry(s string) string {
m := make(map[string]string)
// [snip]
m["Australia"] = "AU"
// [snip]
return m[s]
}
You're not using the right function, the one using the map whose key is a name. You probably want
a := CodeByCountry("Australia")
This works.
But it makes no sense to create the map each time you need it. Take the map creation out of the functions, and put it for example in the init :
package main
import "fmt"
var byCode = make(map[string]string)
var byName = make(map[string]string)
func init() {
m := byCode
m["AF"] = "Afghanistan"
m["AL"] = "Albania"
m["DZ"] = "Algeria"
m["AS"] = "American Samoa"
m = byName
m["Austria"] = "AT"
m["Mozambique"] = "MZ"
m["Solomon Islands"] = "SB"
m["United States"] = "US"
m["Anguilla"] = "AI"
m["Australia"] = "AU"
}
func CountryCode(s string) string {
return byCode[s]
}
func CodeByCountry(s string) string {
return byName[s]
}
func main() {
a := CodeByCountry("Australia")
fmt.Println(a)
}
Another solution for the initialization, as it seems bijective, would be to have one function adding a pair and filling both maps :
func addInMaps(code,name string) {
byCode[code] = name
byName[name] = code
}

Resources