How to create an associative map in golang? - arrays

I'm trying to create an 'associative' map. The issue is that the superMap collects all the values of aMap.
Basically even if I want to store only one instance of amap on each look I endup storing the current instance plus all the previous loops. supermap[value] = amap[value] . However the snippet below stores supermap[value] = amap. The reason is that I can't find any way to fix this. If I delete the old values of aMap after each loop they are deleted from supermap as well.
package main
import (
"fmt"
)
func main() {
aData := map[int]string{
0: "apple",
1: "samsung",
2: "htc",
3: "sony",
}
dir := []string{"user", "doc", "bin", "src"}
aMap := make(map[int]string)
superMap := make(map[string]map[int]string)
for k, v := range dir {
aMap[k] = aData[k]
superMap[v] = aMap
}
hello(superMap)
}
func hello(superMap map[string]map[int]string) {
fmt.Printf("superMap of user is %v \n", superMap["user"])
cnt := len(superMap["user"])
if cnt > 1{
fmt.Printf("expected only one value received %v", cnt)
}
}
Play

As Arjan said in the comment, you need to move the creation of aMap into the for loop. The reason is because, in the original code you posted you are dealing with one instance of aMap in memory. That means, only one map called aMap is created and when you assign another variable to the value of aMap you are assigning a reference. This means, any variable that hold a reference (to aMap) where state is mutated, will be observed in all other variables also holding the reference because they all resolve to the same object in memory.
When the aMap is moved into the for/range loop, this means that 4 individual instances of aMap will be created all with their own memory. Mutating the state of one of those aMaps will not affect the others because they are their own objects in memory. Now, if you took one of those objects and made a reference to it again with another variable then you'd end up in the same boat as the first case.
package main
import (
"fmt"
)
func main() {
aData := map[int]string{
0: "apple",
1: "samsung",
2: "htc",
3: "sony",
}
dir := []string{"user", "doc", "bin", "src"}
//aMap := make(map[int]string) //only one map instance is created in memory
superMap := make(map[string]map[int]string)
for k, v := range dir {
//multiple map instances are created in memory
aMap := make(map[int]string)
aMap[k] = aData[k]
superMap[v] = aMap
}
hello(superMap)
}
func hello(superMap map[string]map[int]string) {
fmt.Printf("superMap of user is %v \n", superMap["user"])
cnt := len(superMap["user"])
if cnt > 1 {
fmt.Printf("expected only one value received %v", cnt)
}
}

Related

Calling valiable name from another variable

Please help to call the variable from another variable.
I have the script which is taking EC2 instances and return in "*ec2.Instance" variable.
I can print information from static text, for example :
fmt.Println(instance.InstanceType) // t3.small
But I have the list of reqired fields like and don't know how dynamic use name from this list :
fields := []string{"InstanceId", "InstanceType", "PrivateIpAddress"}
for i := range fields {
fmt.Println(fields[i])
fmt.Println(instance.fields[i]) // Not correct ... :(
}
You need to use reflection to do this in go.
The key takeaway is you need to "analyze" the returned value at runtime and access properties by name from the "reflected" structure. Reflection basically means analyzing objects at runtime.
package main
import (
"fmt"
"reflect"
)
type whatever struct {
cat string
dog string
animals int
something string
}
func main() {
wantProps := []string{ "cat", "animals"}
we := whatever{cat: "meow", animals: 22}
r := reflect.ValueOf(we)
for _, propName := range wantProps {
prop := r.FieldByName(propName)
fmt.Println(propName, prop)
}
}
More details:
Golang dynamic access to a struct property

Deepcopying map containing pointer to structure

I currently have a map which is as follows
licFeatureMem = make(map[string][]*common.Feature)
I need to make a copy of the map to pass into some function which modified the same so that i don't lose the original copy
modFeatureMem := make(map[string][]*common.Feature)
for key, lst := range licFeatureMem {
var newFtLst []*common.Feature
for _, info := range lst {
newFtLst = append(newFtLst, info)
}
modFeatureMem[key] = &newFtLst
}
What is see is when modFeatureMem is modified, original licFeatureMem also gets modified. Can someone please help me on how to do this?
The copied map has the same pointers to the common.Feature instances the original map has. Any modification to common.Feature will be visible on both maps. To get a truly deep copy, you have to copy those objects as well:
for key, lst := range licFeatureMem {
var newFtLst []*common.Feature
for _, info := range lst {
newInfo:=*info
newFtLst = append(newFtLst, &newInfo)
}
modFeatureMem[key] = &newFtLst
}

Sort while appending to slice?

I have a []byte which I need to sort, in ascending order.
I get an object with the items and then iterate the array in order to create the object returned:
// unfortunately, for some obscure reason I can't change the data types of the caller and the object from the function call are different, although both are []byte underneath (...)
type ID []byte
// in another package:
type ByteInterface []byte
func (c *Store) GetAll() ByteInterface {
returnObj := make([]ByteInterface,0)
obj, err := GetData()
// err handling
for _, b := range obj.IDs {
returnObj = append(returnObj, ByteInterface(b))
}
return returnObj
}
So I'm asking myself if it is possible to do the append so that returnObj is sorted right away, or if I need to sort obj.ByteData upfront (or sort returnOjb afterwards).
On each iteration, do the following:
Grow the target slice (possibly reallocating it):
numElems := len(returnObj)
returnObj = append(returnObj, make([]byte, len(obj))...)
Use the standard approach for insertion to keep the destination sorted by finding a place to put each byte from the source slice, one by one:
for _, b := range obj {
i := sort.Search(numElems, func (i int) bool {
return returnObj[i] >= b
}
if i < numElems {
copy(returnObj[i+1:], returnObj[i:])
}
returnObj[i] = b
numElems++
}
(The call to copy should be optimized by copying less but this is left as an exercise for the reader.)

GoLang Print the Number of Occurances of the Values in an Array

The following is my code where I need to Print the number of occurances of each values in an array.
package main
import "fmt"
func main(){
//Initialize an array
inputArray := []int{10,20,30,56,67,90,10,20}
printUniqueValue(inputArray)
}
func printUniqueValue( arr []int){
//Create a dictionary of values for each element
var dict map[int]int
count := 0
for _ , num := range arr {
dict[num] = count+1
}
fmt.Println(dict)
}
But I couldn't construct the dictionary as I wish like , dict[10] should have value 2.
Sample Expected Output :
dict[10] = 2
dict[20] = 2
dict[30] = 1
Error I got: panic: assignment to entry in nil map
package main
import "fmt"
func main(){
//Initialize an array
inputArray := []int{10,20,30,56,67,90,10,20}
printUniqueValue(inputArray)
}
func printUniqueValue( arr []int){
//Create a dictionary of values for each element
dict:= make(map[int]int)
for _ , num := range arr {
dict[num] = dict[num]+1
}
fmt.Println(dict)
}
This prints map[67:1 90:1 10:2 20:2 30:1 56:1]
You need to initialize dict with a non-nil map; one way would be with var dict = make(map[int]int).
Once you fix that, you will also need to deal with the logic error in dict[num] = count+1, where the count of that value is set to 1 (count is always 0) instead of one more than the previous value.
You have a couple of problems here.
You are declaring dict as a map value, but it is not assigned any value and so is nil
You are not updating the count as you are expecting
To fix the issue with the map, use the make function to assign an empty map to the dict variable. As explained above.
You are expecting:
map[10:2 20:2 30:1 56:1 67:1 90:1]
But even if the map was initialised correctly, you would get:
map[10:1 20:1 30:1 56:1 67:1 90:1]
Don't use a counter outside of the map itself. Use the existing value.
According to the Go tour maps return a zero value when queried with a non-existing key. This is a nice feature for this task, see the below code
package main
import "fmt"
func main(){
inputArray := []int{10,20,30,56,67,90,10,20}
printUniqueValue(inputArray)
}
func printUniqueValue(arr []int) {
dict := make(map[int]int)
for _ , num := range arr {
// dict[num] will return 0 if it hasn't yet been initialised
dict[num] = dict[num] + 1
}
fmt.Println(dict)
}

Count similar array value

I'm trying to learn Go (or Golang) and can't seem to get it right. I have 2 texts files, each containing a list of words. I'm trying to count the amount of words that are present in both files.
Here is my code so far :
package main
import (
"fmt"
"log"
"net/http"
"bufio"
)
func stringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}
func main() {
// Texts URL
var list = "https://gist.githubusercontent.com/alexcesaro/c9c47c638252e21bd82c/raw/bd031237a56ae6691145b4df5617c385dffe930d/list.txt"
var url1 = "https://gist.githubusercontent.com/alexcesaro/4ebfa5a9548d053dddb2/raw/abb8525774b63f342e5173d1af89e47a7a39cd2d/file1.txt"
//Create storing arrays
var buffer [2000]string
var bufferUrl1 [40000]string
// Set a sibling counter
var sibling = 0
// Read and store text files
wordList, err := http.Get(list)
if err != nil {
log.Fatalf("Error while getting the url : %v", err)
}
defer wordList.Body.Close()
wordUrl1, err := http.Get(url1)
if err != nil {
log.Fatalf("Error while getting the url : %v", err)
}
defer wordUrl1.Body.Close()
streamList := bufio.NewScanner(wordList.Body)
streamUrl1 := bufio.NewScanner(wordUrl1.Body)
streamList.Split(bufio.ScanLines)
streamUrl1.Split(bufio.ScanLines)
var i = 0;
var j = 0;
//Fill arrays with each lines
for streamList.Scan() {
buffer[i] = streamList.Text()
i++
}
for streamUrl1.Scan() {
bufferUrl1[j] = streamUrl1.Text()
j++
}
//ERROR OCCURRING HERE :
// This code if i'm not wrong is supposed to compare through all the range of bufferUrl1 -> bufferUrl1 values with buffer values, then increment sibling and output FIND
for v := range bufferUrl1{
if stringInSlice(bufferUrl1, buffer) {
sibling++
fmt.Println("FIND")
}
}
// As a testing purpose thoses lines properly paste both array
// fmt.Println(buffer)
// fmt.Println(bufferUrl1)
}
But right now, my build doesn't even succeed. I'm only greeted with this message:
.\hello.go:69: cannot use bufferUrl1 (type [40000]string) as type string in argument to stringInSlice
.\hello.go:69: cannot use buffer (type [2000]string) as type []string in argument to stringInSlice
bufferUrl1 is an array: [4000]string. You meant to use v (each
string in bufferUrl1). But in fact, you meant to use the second
variable—the first variable is the index which is ignored in the code
below using _.
type [2000]string is different from []string. In Go, arrays and slices are not the same. Read Go Slices: usage and internals. I've changed both variable declarations to use slices with the same initial length using make.
These are changes you need to make to compile.
Declarations:
// Create storing slices
buffer := make([]string, 2000)
bufferUrl1 := make([]string, 40000)
and the loop on Line 69:
for _, s := range bufferUrl1 {
if stringInSlice(s, buffer) {
sibling++
fmt.Println("FIND")
}
}
As a side-note, consider using a map instead of a slice for buffer for more efficient lookup instead of looping through the list in stringInSlice.
https://play.golang.org/p/UcaSVwYcIw has the fix for the comments below (you won't be able to make HTTP requests from the Playground).

Resources