Deepcopying map containing pointer to structure - arrays

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
}

Related

Search in struct array

I have a struct defined like this :
type Issues struct {
RedmineIssue string
GitlabIssue string
}
Then I get the list from the DB
database.Find(&Issues)
Then I have another array
redmineIssues []redmine.Issue
Is there any way to search issues in my array Issues that are also in the array redmineIssues base on field RedmineIssue ( string ) ?
Today here is what I'm doing
database.Find(&Issues)
redmineIssue := []string{}
for _, issueRedmine := range Issues {
redmineIssue = append(redmineIssue, issueRedmine.RedmineIssue)
}
gitlabissues := []string{}
for _, issueGitlab := range Issues {
gitlabissues = append(gitlabissues, issueGitlab.GitlabIssue)
}
Then I can compare with another array I have
for _, issueR := range IssueFromRedmineWS {
inArray, _ := in_array(issueR.Id, redmineIssue)
if !inArray {
// The issue is not in the DB
}
}
Any idea on how to optimize this and to make it cleaner?
A for loop is the right way to go. Assuming the code you pasted actually works, you can merge those into a single for loop, the way you have it written.
database.Find(&Issues)
redmineIssue := []string{}
gitlabissues := []string{}
for _, issue := range Issues {
redmineIssue = append(redmineIssue, issue.RedmineIssue)
gitlabissues = append(gitlabissues, issue.GitlabIssue)
}
But this assumes that Issues is an array. Which doesn't match the rest of your question. According to your stated definition of Issues, the code you posted won't even compile, though. So I don't know if this code will work for you, either.
You can provide another micro-optimization by pre-allocating the arrays:
database.Find(&Issues)
redmineIssue := make([]string{}, 0, len(Issues))
gitlabissues := make([]string{}, 0, len(Issues))

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.)

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).

How to set new value to struct member of explicit type from interface{} value (reflection)? Golang

I want to understand some subtle moments of using reflect package. Please, see example below, it describes better what I want to know:
type Robot struct {
id int
model string
}
func change(i interface{}, fields ...string) {
v := reflect.ValueOf(i).Elem()
// here I emulate function by slice that could return any value,
// so here I need to check if I can store incoming values to existing struct
returns := []interface{}{100, "Something"}
for i, name := range fields {
x := reflect.ValueOf(&returns[i]).Elem()
//check if value representing x is the same of struct member
v.FieldByName(name).Set(x)
// ^ here I want to store 100 to Robot.id when i = 0,
// and "Something" to Robot.model when i = 1
}
}
func main() {
robot := &Robot{id: 1, model: "T310"}
change(robot, "model", "id")
// now robot become as follows: &Robot{100, "Something"}
}
Why does it need for?
// It is need for retrieving values from sql DB into struct members
// (only for training purposes :))
// Example:
f := func(q string, structs interface{}, fields ...string) {
rows, _ := db.Query(q)
for i := 0; rows.Next(); i++ {
rows.Scan(&structs[i])
// very dirty here! it's hard to understand how to implement it
}
}
var robots = []*Robot
f("select id, model from robots", robots, "id", "model")
// now each member of robots var should contain values from DB
I tried to be descriptive and explain as short as possible. I hope you understand me..
You can only set exported fields via reflection, so capitalize those first. Otherwise, if you're counting on positional values, make sure they are properly aligned.
Something like this for example: http://play.golang.org/p/ItnjwwJnxe
type Robot struct {
ID int
Model string
}
func change(i interface{}, fields ...string) {
returns := []interface{}{100, "Something"}
v := reflect.ValueOf(i).Elem()
for i, name := range fields {
val := reflect.ValueOf(returns[i])
v.FieldByName(name).Set(val)
}
}
func main() {
robot := &Robot{ID: 1, Model: "T310"}
fmt.Println(robot)
change(robot, "ID", "Model")
fmt.Println(robot)
}

How to create an associative map in golang?

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)
}
}

Resources