Better way to store references in a map - go

What would be a better way than this to store references to structs in a map? Right now I'm using an anonymous function so that all the map keys don't end up with the same reference. I'm sure there has to be a more elegant way to do it.
m := make(map[string]*Result)
for result := range results {
func(r Result) {
m[r.Key] = &r
}(result)
}
Update: results is a channel of simple structs

Use the following to store a pointer to a newly allocated value:
m := make(map[string]*Result)
for r := range results {
r := r
m[r.Key] := &r
}

If you're trying to copy the result struct:
m := make(map[string]*Result)
for result := range results {
result:=result
m[result.Key] = &result
}

Related

Concurrency-safe map of slices

I have a type that contains a sync.Map where the key in the map is a string and the value is a slice. My code for inserting items into the map is as follows:
newList := []*Item{item}
if result, ok := map.LoadOrStore(key, newList); ok {
resultList := result.([]*Item)
resultList = append(resultList, item)
map.Store(key, resultList)
}
This is not concurrency-safe because the the slice can be loaded and modified by multiple calls concurrently. This code is very fragile so I've attempted to modify it to be:
newList := []*Item{item}
if result, ok := map.LoadOrStore(key, &newList); ok {
resultList := result.(*[]*Item)
*resultList = append(*resultList, item)
}
All this does is make the issues occur deterministically. So, I'm trying to find a way to have a map-of-slices that can be added to concurrently. My instinct is to use sync.Mutex to lock the list while I'm adding to it but in order to maintain the concurrent access to the sync.Map I would need to create a map of sync.Mutex objects as well, like this:
newLock := sync.Mutex{}
raw, _ := lockMap.LoadOrStore(key, &newLock)
lock := raw.(*sync.Mutex)
newList := []*Item{item}
if result, ok := map.LoadOrStore(key, &newList); ok {
lock.Lock()
resultList := result.(*[]*Item)
*resultList = append(*resultList, item)
lock.Unlock()
}
Is there an easier way to go about this?
It isn't very different from your current plan, but you could save yourself the trouble of handling two maps by using a struct with an embedded mutex for the values of the map.
The struct would look something like this:
type SafeItems struct {
sync.Mutex
Items []*Item
}
And it could be used like this:
newMapEntry := SafeItems{Items: itemPtrList}
if result, ok := map.LoadOrStore(key, &newMapEntry); ok {
mapEntry := result.(*SafeItems)
mapEntry.Lock()
mapEntry.Items = append(mapEntry.Items, item)
mapEntry.Unlock()
}
It's not a huge change but it does provide some syntactic sugar.

Go: A function that would consume maps with different types of values

In my code, I need a function that would return an ordered slice of keys from a map.
m1 := make(map[string]string)
m2 := make(map[string]int)
And now I need to call a function passing both types of maps:
keys1 := sortedKeys(m1)
keys2 := sortedKeys(m1)
Problem: I have to write two functions because the function should consume maps of two different types. At the same time, the body of the function will be the same in both cases.
Question: How can I use a single implementation for two maps? Or is there any other way of solving the problem in an elegant way?
My first idea was to use map[string]interface{} as an argument type, but you can't assign neither map[string]string, nor map[string]int to it.
My code:
func sortedKeys(m map[string]string) []string {
var keys []string
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}
I would have to repeat the same code but for map[string]int.
You can use interface{} and use reflection for achieving this.
You can write two functions for the same but it is just not scalable, say, you are supporting string and int now but you wish to support int64, float64, bool or struct in the future. Having a common function using map[string]interface{} and using reflection is the way to go.
Suggested Code :
package main
import (
"fmt"
"reflect"
)
func main() {
m1 := make(map[string]string)
m2 := make(map[string]int)
m1["a"] = "b"
m1["b"] = "c"
m2["a"] = 1
m2["b"] = 2
fmt.Println(sortedKeys(m1))
fmt.Println(sortedKeys(m2))
}
// Returns slice of values in the type which is sent to it
func sortedKeys(m interface{}) interface{} {
if m == nil {
return nil
}
if reflect.TypeOf(m).Kind() != reflect.Map {
return nil
}
mapIter := reflect.ValueOf(m).MapRange()
mapVal := reflect.ValueOf(m).Interface()
typ := reflect.TypeOf(mapVal).Elem()
outputSlice := reflect.MakeSlice(reflect.SliceOf(typ), 0, 0)
for mapIter.Next() {
outputSlice = reflect.Append(outputSlice, mapIter.Value())
}
return outputSlice.Interface()
}
Output :
[b c]
[1 2]
https://play.golang.org/p/2fkpydH9idG

How do we create an empty map and append new data in golang?

I'm having a problem with creating an empty map and append new data to it while looping on another map.
this is the error i'm getting on my IDE.
here's my data struct to be added to the map.
type Outcome struct {
QuestionIndex string
ChoiceIndex int64
Correct bool
}
func createEntryOutcome(e *entry.Entry) map[string]interface{} {
entryPicks := e.Live.Picks
outcomes := make(map[string]interface{})
for idx, pick := range entryPicks {
mappedPick := pick.(map[string]interface{})
outcomes = append(outcomes, Outcome{
QuestionIndex: idx,
ChoiceIndex: mappedPick["index"].(int64),
Correct: mappedPick["correct"].(bool),
})
}
return outcomes
}
i basically want something like below to be saved in the database.
[
{
qIndex: "1",
cIndex: 1,
correct: false,
},
{
qIndex: "1",
cIndex: 1,
correct: false,
},
]
im new to golang and any help is appreciated. thanks
As the error clearly says:
first argument to append must be slice; have map[string]interface{}
which means you need to create a slice before appending the data to outcomes which is actually slice of outcomes, like you have mentioned in the output you want.
The append function appends the elements x to the end of the slice s,
and grows the slice if a greater capacity is needed.
Create a slice of outcomes and then append the data from entryPicks to that slice:
outcomes := make([]map[string]interface{})
for idx, pick := range entryPicks {
mappedPick := pick.(map[string]interface{})
outcomes = append(outcomes, Outcome{
QuestionIndex: idx,
ChoiceIndex: mappedPick["index"].(int64),
Correct: mappedPick["correct"].(bool),
})
}
which will let you provide the outcome you want.
type Outcome struct {
QuestionIndex string
ChoiceIndex int64
Correct bool
}
func createEntryOutcome(e *entry.Entry) map[string]interface{} {
entryPicks := e.Live.Picks
var outcomes []Outcome
for idx, pick := range entryPicks {
mappedPick := pick.(map[string]interface{})
outcomes = append(outcomes, Outcome{
QuestionIndex: idx,
ChoiceIndex: mappedPick["index"].(int64),
Correct: mappedPick["correct"].(bool),
})
}
return outcomes
}
change outcomes := make(map[string]interface{}) to var outcomes []Outcome

Initialize structs from list of strings

I'm trying to initialize structs from a list of strings, but the compiler is throwing the following error. I'm still learning the language so excuse my ignorance, but is this solved by utilizing type assertion?
ERROR: v.UberX undefined (type string has no field method UberX)
type Galaxy struct {
UberX int64
UberY int64
}
func main() {
galaxies := []string{"andromeda", "milkyway", "maffei"}
for _, v := range galaxies {
v := &Galaxy{}
}
for _, v := range galaxies {
v.UberX += 1000
v.UberY += 750
}
}
Your Galaxy struct doesn't even store the name, in your attempt there isn't any connection between the names and the struct values. Add the name to the struct:
type Galaxy struct {
Name string
UberX int64
UberY int64
}
Next, in your first loop you create a *Galaxy value, but you only store it in a local variable v which by the way shadows the loop variable v:
for _, v := range galaxies {
v := &Galaxy{}
}
You need a slice of Galaxy or a slice of *Galaxy which you can populate:
gs := make([]*Galaxy, len(galaxies))
Then 1 loop is enough to loop over the galaxy names and populate the gs slice:
for i, v := range galaxies {
gs[i] = &Galaxy{
Name: v,
UberX: 1000,
UberY: 750,
}
}
Verifying the result:
for _, v := range gs {
fmt.Printf("%+v\n", v)
}
Output (try it on the Go Playground):
&{Name:andromeda UberX:1000 UberY:750}
&{Name:milkyway UberX:1000 UberY:750}
&{Name:maffei UberX:1000 UberY:750}
Recommended to go through the Golang Tour first to learn the basics.

Is there a way to write generic code to find out whether a slice contains specific element in Go?

I want to know is there a generic way to write code to judge whether a slice contains an element, I find it will frequently useful since there is a lot of logic to fist judge whether specific elem is already in a slice and then decide what to do next. But there seemed not a built-in method for that(For God's sake, why?)
I try to use interface{} to do that like:
func sliceContains(slice []interface{}, elem interface{}) bool {
for _, item := range slice {
if item == elem {
return true
}
}
return false
}
I thought interface{} is sort of like Object of Java, but apparently, I was wrong. Should I write this every time meet with a new struct of slice? Isn't there a generic way to do this?
You can do it with reflect, but it will be MUCH SLOWER than a non-generic equivalent function:
func Contains(slice, elem interface{}) bool {
sv := reflect.ValueOf(slice)
// Check that slice is actually a slice/array.
// you might want to return an error here
if sv.Kind() != reflect.Slice && sv.Kind() != reflect.Array {
return false
}
// iterate the slice
for i := 0; i < sv.Len(); i++ {
// compare elem to the current slice element
if elem == sv.Index(i).Interface() {
return true
}
}
// nothing found
return false
}
func main(){
si := []int {3, 4, 5, 10, 11}
ss := []string {"hello", "world", "foo", "bar"}
fmt.Println(Contains(si, 3))
fmt.Println(Contains(si, 100))
fmt.Println(Contains(ss, "hello"))
fmt.Println(Contains(ss, "baz"))
}
How much slower? about x50-x60 slower:
Benchmarking against a non generic function of the form:
func ContainsNonGeneic(slice []int, elem int) bool {
for _, i := range slice {
if i == elem {
return true
}
}
return false
}
I'm getting:
Generic: N=100000, running time: 73.023214ms 730.23214 ns/op
Non Generic: N=100000, running time: 1.315262ms 13.15262 ns/op
You can make it using the reflect package like that:
func In(s, e interface{}) bool {
slice, elem := reflect.ValueOf(s), reflect.ValueOf(e)
for i := 0; i < slice.Len(); i++ {
if reflect.DeepEqual(slice.Index(i).Interface(), elem.Interface()) {
return true
}
}
return false
}
Playground examples: http://play.golang.org/p/TQrmwIk6B4
Alternatively, you can:
define an interface and make your slices implement it
use maps instead of slices
just write a simple for loop
What way to choose depends on the problem you are solving.
I'm not sure what your specific context is, but you'll probably want to use a map to check if something already exists.
package main
import "fmt"
type PublicClassObjectBuilderFactoryStructure struct {
Tee string
Hee string
}
func main() {
// Empty structs occupy zero bytes.
mymap := map[interface{}]struct{}{}
one := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "hey"}
two := PublicClassObjectBuilderFactoryStructure{Tee: "hola", Hee: "oye"}
three := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "again"}
mymap[one] = struct{}{}
mymap[two] = struct{}{}
// The underscore is ignoring the value, which is an empty struct.
if _, exists := mymap[one]; exists {
fmt.Println("one exists")
}
if _, exists := mymap[two]; exists {
fmt.Println("two exists")
}
if _, exists := mymap[three]; exists {
fmt.Println("three exists")
}
}
Another advantage of using maps instead of a slice is that there is a built-in delete function for maps. https://play.golang.org/p/dmSyyryyS8
If you want a rather different solution, you might try the code-generator approach offered by tools such as Gen. Gen writes source code for each concrete class you want to hold in a slice, so it supports type-safe slices that let you search for the first match of an element.
(Gen also offers a few other kinds of collection and allows you to write your own.)

Resources