How to assign a pointer to a Go map - pointers

I am having problems assigning a pointer to a map. Maybe this is a bug in Go? Or maybe I am just doing something wrong. The code is here on the playground as well, https://play.golang.org/p/p0NosPtkptz
Here is some super simplified code that illustrates the problem. I am creating an object called collections that has two collection objects in it. I am then looping through those collections and assigning them to a map where the key in the map is the collection ID.
package main
import (
"fmt"
)
type collection struct {
ID string
Name string
}
type collections struct {
Collections []collection
}
type cache struct {
Index int
Collections map[string]*collection
}
func main() {
var c cache
c.Collections = make(map[string]*collection)
// Create 2 Collections
var col1, col2 collection
col1.ID = "aa"
col1.Name = "Text A"
col2.ID = "bb"
col2.Name = "Test B"
// Add to Collections
var cols collections
cols.Collections = append(cols.Collections, col1)
cols.Collections = append(cols.Collections, col2)
fmt.Println("DEBUG Collections Type", cols)
i := 0
for k, v := range cols.Collections {
c.Index = i
c.Collections[v.ID] = &v
fmt.Println("DEBUG k", k)
fmt.Println("DEBUG v", v)
i++
}
fmt.Println("Collection 1", c.Collections["aa"].ID)
fmt.Println("Collection 2", c.Collections["bb"].ID)
fmt.Println(c)
}
The output from this playground code looks like:
DEBUG Collections Type {[{aa Text A} {bb Test B}]}
DEBUG k 0
DEBUG v {aa Text A}
DEBUG k 1
DEBUG v {bb Test B}
Collection 1 bb
Collection 2 bb
{1 map[aa:0x1040a0f0 bb:0x1040a0f0]}
So it seems like the map is for some reason getting the same pointer for each entry. All of the "DEBUG" lines print out what I would expect. However, the three print lines at the very end, do not. Collection 1 should be "aa" not "bb".

When you are putting &v into c.Collections[v.ID], you are actually assigning same address of loop variable v.
This address finally holds the last value of your list. That's why you are getting bb Test B for all key.
Print these value and you will see same address.
fmt.Printf("%p\n", c.Collections["aa"])
fmt.Printf("%p\n", c.Collections["bb"])
And by copying it to a new variable in the loop scope, the issue is solved. Each step in the loop you will put a new and unique address into the cache.
for k, v := range cols.Collections {
coll := v
c.Collections[v.ID] = &coll
fmt.Println("DEBUG k", k)
fmt.Println("DEBUG v", v)
i++
}

Unfortunately, the code in the first answer has an error - a pointer to a local variable coll:
for k, v := range cols.Collections {
coll := v
c.Collections[v.ID] = &coll
Just try to change a property value of a collection:
cols.Collections[0].Name = "Text C"
fmt.Println("Collection 1", cols.Collections[0].Name)
fmt.Println("Collection 1", c.Collections["aa"].Name)
// Collection 1 Text C
// Collection 1 Text A
But the another code will print an expected result:
for k, v := range cols.Collections {
c.Index = i
p := &cols.Collections[k]
c.Collections[v.ID] = p
....
cols.Collections[0].Name = "Text C"
fmt.Println("Collection 1", cols.Collections[0].Name)
fmt.Println("Collection 1", c.Collections["aa"].Name)
// Collection 1 Text C
// Collection 1 Text C

The answer about same pointer is a map contains the address of a variable v, but not array items.
for k, v := range cols.Collections {
......
c.Collections[v.ID] = &v
A right solution is storing a pointer to array, but not a value.
type collections struct {
Collections []*collection
}
cols.Collections = append(cols.Collections, &col1)
cols.Collections = append(cols.Collections, &col2)
Or get address of map item (as the answer higher)
for k, v := range cols.Collections {
c.Index = i
p := &cols.Collections[k]
c.Collections[v.ID] = p

Related

Insert objects in specific order with map

I have a use case where the order of objects needs to be in a specific order. The current implementation is done with using map and I've found numerous posts and articles where it states that map are an unordered list. All of the solutions that I found are those where they've made the keys as integers and they've used sort.Ints(keys) to sort by keys.
In the code, I'm using a yaml template to instantiate a dictionary pair, then passing it into the ProcessFruits function where it does the logic.
How would I go about getting the desired result (see below) where the object from the top of the list in fruits.yml.tmpl will always be first?
Here's a simplified version of my code:
//Filename: fruits.yml.tmpl
fruits: {{ $fruits := processFruits
"oranges" true
"bananas" false
"apples" true
}}
{{ $fruits }}
//Filename: fruits.go
func ProcessFruits(fruits map[string]interface{}) (interface{}) {
keys := make([]string, len(fruits))
i := 0
for fruit := range fruits {
keys[i] = fruit
i++
}
sort.Strings(keys)
fmt.Println(keys)
}
// Connect fruits.yml.tmpl to the ProcessFruits function
tmpl, err := template.New(t).Funcs(template.FuncMap(map[string]interface{}{
"processFruits": ProcessFruits,
})).Funcs(sprig.TxtFuncMap())
Actual Results:
[apples:true bananas:false oranges:true]
Desired Results:
[oranges:true bananas:false apples:true]
Go Playground
https://go.dev/play/p/hK2AdRVsZXJ
You are missing the usage of sort.Reverse() and sort.StringSlice()
func main() {
keys := []string{"bananas", "apples", "oranges"}
sort.Sort(sort.Reverse(sort.StringSlice(keys)))
fmt.Println(keys)
}
https://go.dev/play/p/n08S7xtbeij
See: https://pkg.go.dev/sort#example-Reverse
The arguments are passed as a slice. Collect every other argument as a string and print:
func ProcessFruits(args ...interface{}) interface{} {
var fruits []string
for i, arg := range args {
if i%2 == 0 {
fruits = append(fruits, arg.(string))
}
}
fmt.Println(fruits)
return nil
}
Not the prettiest solution, but I think I've figured out a working code to my problem. What I've done was creating another dictionary that will keep track of the order of the "fruits", then combining the two dictionary together with a nested for loop and output the result to a slice.
Here's my code:
package main
import (
"fmt"
"sort"
)
func ProcessFruits(fruits map[string]interface{}, counts map[int]string) {
keys := make([]string, len(fruits))
var foo []string
var baz []int
for k := range fruits {
foo = append(foo, k)
}
for _, k := range foo {
fmt.Println("Key-string:", k, "Value-bool:", fruits[k])
}
fmt.Println("==========================")
// Iterate over counts (keys are ints)
for l := range counts {
baz = append(baz, l)
}
sort.Ints(baz)
for _, l := range baz {
fmt.Println("Key-int:", l, "Value-string:", counts[l])
}
fmt.Println("==========================")
// Correlate list with sorted integer keys with the other list that contains true/false
i := 0
for _, m := range baz {
for _, n := range foo {
//fmt.Println("Key-int:", m, "Value-string:", counts[m])
//fmt.Println("Key-string:", n, "Value-bool:", fruits[n])
if counts[m] == n {
keys[i] = n
i++
//fmt.Println(i)
}
}
}
// Desired results is now in the slice, keys.
fmt.Println(keys)
}
func main() {
var m = map[string]interface{}{
"oranges": true,
"bananas": false,
"apples": true,
}
var n = map[int]string{
0: "oranges",
1: "bananas",
2: "apples",
}
ProcessFruits(m, n)
}
If anyone has a better solution, then I'd be curious to know.

Build a nested map of arbitrary depth from an array of keys and a value

I'd like to be able to write a GoLang function to take an array of keys and a value (i.e. keys={"a", "b", "c"}, value=123) then build a data-structure of nested maps where the positional index in the array corresponds to the depth in the nested map, and the value is assigned to the last key. For example, given the above keys and value, I want to build the following dictionary structure
{"a":{"b":{"c":123}}}
Below is the code I currently have. The issue is that the resulting map is as follows
{"a":{}, "b":{}, "c":123}.
Any advice as to how I should amend this/why it's happening would be much appreciated.
import (
"fmt"
)
type dict map[interface{}]interface{}
func main() {
vals := []interface{}{"a", "b", "c"}
// create a dictionary
d := make(dict)
d.Set(vals, 123)
// print it
fmt.Println(d)
}
func (d dict) Set(keys []interface{}, value interface{}) {
d2 := d
fmt.Println("Initial dict: ", d2)
keylen := len(keys)-1
for _, key := range keys[:keylen] {
// if key exists, recurse into that part of the dict
if entry, ok := d2[key]; ok {
d2 := entry
fmt.Println("Entered level in dict: ", d2)
} else {
d3 := make(dict)
d2[key] = d3
d2 := d3
fmt.Println("Created new level in dict: ", d2)
}
}
d2[keys[keylen]] = value
fmt.Println("Final dict: ", d2)
}
You seem to have overcomplicated your solution. This recursive algorithm should do:
func set(d dict,keys []interface{}, value interface{}) {
if len(keys)==1 {
d[keys[0]]=value
return
}
v, ok:=d[keys[0]]
if !ok {
v=dict{}
d[keys[0]]=v
}
set(v.(dict),keys[1:],value)
}
You have to add code to handle the case where you're resetting a value (i.e. when v.(dict) type assertion might fail). Otherwise, you can recursively descend the map, and consume the keys at the same time.

A safe way to add a key to map in Golang

Due to this fact:
If a map entry is created during iteration, that entry may be produced
during the iteration or may be skipped. The choice may vary for each
entry created and from one iteration to the next.
It's not safe to add key-values to map during iteration:
var m = make(map[string]int)
m["1"] = 1
m["2"] = 2
m["3"] = 3
for k, v := range m {
if strings.EqualFold( "2", k){
m["4"] = 4
}
fmt.Println(k, v)
}
Sometimes "4" key is produced, sometimes not.
What is the workaround to make it always produced?
Create another map with the items you want to add to the original map, and after the iteration, you merge them.
var m = make(map[string]int)
m["1"] = 1
m["2"] = 2
m["3"] = 3
var n = make(map[string]int)
for k := range m {
if strings.EqualFold("2", k) {
n["4"] = 4
}
}
for k, v := range n {
m[k] = v
}
for _, v := range m {
fmt.Println(v)
}

Iterate through a map of interface that contains different levels of maps

So lets say I have a map of interface like this:
c := map[string]interface{} {
"test": test,
"test2": test2,
}
Assuming that test is a map[string]map[string]map[string]string and test2 is map[string]string.
How would I create a for loop that would enumerate each index of the map and would enumerate through each index's map also?
So far I have gotten:
func sanitize_map(m map[string]interface{}) map[string]interface{} {
for k, v := range m {
//Here is where I want to enumerate through the map of k
}
return m
}
No need for reflection; use a type assertion and pass the value back to your sanitize function
func sanitizeMap(m map[string]interface{}) map[string]interface{} {
for k, v := range m {
_ = k
if v, ok := v.(map[string]interface{}); ok {
sanitizeMap(v)
}
}
return m
}
You can use reflect:
import "reflect"
func sanitize_map(m map[string]interface{}) map[string]interface{} {
for k, v := range m {
// Do something with the key k
kind := reflect.ValueOf(v).Kind()
if kind == reflect.Map {
// You have to be sure the value is of type map[string]interface{}
newValue := v.(map[string]interface{})
// recursively call sanitize
sanitize_map(newValue)
}
}
return m
}
The carveat is: every value in the map has to be either not a map (atom) or a map[string]interface{}. Note map[string]interface{} and map[string]map[string]interface{} are completely unrelated types, and you cannot use a type assertion of the second type on the first one.
However, you can put a map[string]map[string]string in a map[string]interface{}, like this:
innerMap1 := make(map[string]interface{})
// map to strings
innerMap1["a"] = "String 1"
innerMap2 := make(map[string]interface{})
// set mappings, maybe to other types
outerMap := make(map[string]interface{})
outerMap["ABC"] = innerMap1
outerMap["DEF"] = innerMap2
Now you can pass outerMap to the function, and reflect will automatically "strip" the layers of maps for you.

initialising a 2-dim Array in Scala

(Scala 2.7.7:) I don't get used to 2d-Arrays. Arrays are mutable, but how do I specify a 2d-Array which is - let's say of size 3x4. The dimension (2D) is fixed, but the size per dimension shall be initializable. I tried this:
class Field (val rows: Int, val cols: Int, sc: java.util.Scanner) {
var field = new Array [Char](rows)(cols)
for (r <- (1 to rows)) {
val line = sc.nextLine ()
val spl = line.split (" ")
field (r) = spl.map (_.charAt (0))
}
def put (row: Int, col: Int, c: Char) =
todo ()
}
I get this error:
:11: error: value update is not a member of Char
field (r) = spl.map (_.charAt (0))
If it would be Java, it would be much more code, but I would know how to do it, so I show what I mean:
public class Field
{
private char[][] field;
public Field (int rows, int cols, java.util.Scanner sc)
{
field = new char [rows][cols];
for (int r = 0; r < rows; ++r)
{
String line = sc.nextLine ();
String[] spl = line.split (" ");
for (int c = 0; c < cols; ++c)
field [r][c] = spl[c].charAt (0);
}
}
public static void main (String args[])
{
new Field (3, 4, new java.util.Scanner ("fraese.fld"));
}
}
and fraese.fld would look, for example, like that:
M M M
M . M
I get some steps wide with
val field = new Array [Array [Char]](rows)
but how would I then implement 'put'? Or is there a better way to implement the 2D-Array. Yes, I could use a one-dim-Array, and work with
put (y, x, c) = field (y * width + x) = c
but I would prefer a notation which looks more 2d-ish.
for (r <- (1 to rows)) {
Should this be:
for (r <- (0 to rows - 1)) {
... starting from 0 instead of 1?
field (r) = spl.map (_.charAt (0))
Should this use the operator syntax, like this:
field (r) = spl map (_.charAt (0))
... without the '.' between spl and map?
This is my version - I replaced the Scanner with an Array[String] since I'm not really sure what the input for the scanner is supposed to be. It compiles and runs on Scala 2.7.5:
class Field (val rows: Int, val cols: Int, lines: Array[String]) {
var field = new Array [Array[Char]](rows)
// These get replaced later on, but this is how to initialize a 2D array.
for (i <- (0 to rows - 1)) {
field(i) = new Array[Char](cols)
}
for (r <- (0 to rows - 1)) {
val line = lines(r)
val spl = line.split (" ")
field(r) = spl map (_.charAt (0))
}
}
var lines = Array[String] ("A A A A A", "B B B B B", "C C C C C", "D D D D D", "E E E E E")
var test = new Field(5, 5, lines)
test.field

Resources