Go: append directly to slice found in a map - go

I wanted to create a map of slices where values are appended to the corresponding slice. However, when trying to append directly to the slice returned by accessing it (see comment below), it would not be stored, so I had to go with the long form access (line below the comment).
Why is it so? I expected the access to the map to return some sort of pointer, so in my mind mappedAminoAcid == aminoAcidsToCodons[aminoAcid]; clearly, I'm wrong.
Thanks!
aminoAcidsToCodons := map[rune][]string{}
for codon, aminoAcid := range utils.CodonsToAminoAcid {
mappedAminoAcid, ok := aminoAcidsToCodons[aminoAcid]
if ok {
// NOT WORKING: mappedAminoAcid = append(mappedAminoAcid, codon)
aminoAcidsToCodons[aminoAcid] = append(mappedAminoAcid, codon)
} else {
aminoAcidsToCodons[aminoAcid] = []string{codon}
}
}

append returns a new slice if the underlying array has to grow to accomodate the new element. So yes, you have to put the new slice back into the map. This is no different from how strings work, for instance:
var x map[string]string
x["a"] = "foo"
y := x["a"]
y = "bar"
// x["a"] is still "foo"
Since a nil slice is a perfectly fine first argument for append, you can simplify your code to:
aminoAcidsToCodons := map[rune][]string{}
for codon, aminoAcid := range utils.CodonsToAminoAcid {
aminoAcidsToCodons[aminoAcid] = append(aminoAcidsToCodons[aminoAcid], codon)
}

Related

Append content to slice into a nested struct does not work

I have two nested structs like this:
type Block struct {
ID string
Contents []string
}
type Package struct {
Name string
Blocks []Block
}
Original package (p) does not change when I try to append a new Content in a specific block.
for _, b := range p.Blocks {
if b.ID == "B1" {
fmt.Println("Adding a new content")
b.Contents = append(b.Contents, "c3")
}
}
Example:
https://play.golang.org/p/5hm6RjPFk8o
This is happening because this line:
for _, b := range p.Blocks {
creates a copy of each element in the slice, and in this case this means creating a copy of each Block in the slice. So when you then make the changes in the loop body, you are making them to the copy of the Block, instead of to the Block in the slice.
If you instead use the index to get a pointer to each Block, e.g.
for i := range p.Blocks {
b := &p.Blocks[i]
// modify b ...
}
it works as expected:
https://play.golang.org/p/h_nXEX9oWRT
Alternatively, you can make the changes to the copy (as in your original code), and then copy the modified value back to the slice:
for i, b := range p.Blocks {
// modify b ...
p.Blocks[i] = b
}
https://go.dev/play/p/kVHTk-OTyC3
Even further, you could instead store pointers to Block in the slice (instead of the Block themselves), in which case your loop would be making a copy of the pointer, which is a valid way to access the Block the original pointers points to:
https://go.dev/play/p/I9-EyV_iCNS
When you are looping over a slice, each of the individual values retrieved from the slice is a copy of the corresponding element in the slice. So to modify the element in the slice, instead of the copy, you can access the element directly using the indexing expression. Or you can use pointers. Note that pointers are also copied but the copied pointer will point to the same address as the element in the slice and therefore can be used to directly modify the same data.
You can use indexing:
for i := range p.Blocks {
if p.Blocks[i].ID == "B1" {
fmt.Println("Adding a new content")
p.Blocks[i].Contents = append(p.Blocks[i].Contents, "c3")
}
}
https://play.golang.org/p/di175k18YQ9
Or you can use pointers:
type Block struct {
ID string
Contents []string
}
type Package struct {
Name string
Blocks []*Block
}
for _, b := range p.Blocks {
if b.ID == "B1" {
fmt.Println("Adding a new content")
b.Contents = append(b.Contents, "c3")
}
}
https://play.golang.org/p/1RjWlCZkhYv

How to slice a slice for eliminating matching values from identical slice

I'm looking for an easy way to iterate through a slice and on every value that's present in the current slice, remove the element from another slice.
I have a struct:
a := enter{
uid: 1234,
status: []StatusEntry{
{
rank: 1,
iterate: ierationState_Ongoing,
},
{
rank: 2,
iterate: ierationState_Completed,
},
},
}
In my .go file, I have a constant
Steps = [5]int64{0,1,2,3,4}
According to my requirement I want to copy the Steps in another variable and perform remove operation :
Steps2 := Steps // Make a copy of Steps
for _, element := enter.status {
// Remove that element from Steps
}
But I find it difficult to do so since Golang doesn't give me direct method to iterate and remove every element from enter.status from Steps.
I tried multiple things like creating a removeIndex function as posted on various stackoverflow answers like this:
for i, element := enter.status {
Steps2 = removeIndex(enter.status, i)
}
func removeIndex(s []int, index int) []int {
ret := make([]int, 0)
ret = append(ret, s[:index]...)
return append(ret, s[index+1:]...)
}
But it doesn't make sense to use this because I'm trying to remove a matching value (element) and not a specific index (for eg index 5) from Steps2.
Basically, for every element that's in slice enter.status, I want to remove that element/value from slice Steps2
Careful:
[5]int64{0,1,2,3,4}
This is an array (of 5 ints), not a slice. And:
Steps2 := Steps
If Steps were a slice, this would copy the slice header without copying the underlying array.
In any case, given some slice s of type T and length len(s), if you are allowed to modify s in place and order is relevant, you generally want to use this algorithm:
func trim(s []T) []T {
out := 0
for i := range s {
if keep(s[i]) {
s[out] = s[i]
out++
}
}
return s[:out]
}
where keep is your boolean function to decide whether to keep an element. To make this produce a new slice, allocate an output slice of the appropriate length (len(s)) at the start and optionally shrink it later, or, if you expect to throw out most elements, make it empty at the start and use append.
When the keep function is "the value of some field in the output slice does not match the value of any earlier kept field" and the type of that field is usable as a key type, you can use a simple map[T2]struct{} to determine whether the value has occurred yet:
seen := make(map[T2]struct{}, len(s))
and then the keep test and copy sequence becomes:
_, ok := seen[s[i].field]
if !ok {
seen[s[i].field] = struct{}{}
s[out] = s[i]
out++
}
The initial size of seen here is optimized on the theory that most values will be kept; if most values will be discarded, make the map initially empty, or small.

Golang fill slice in function not working

I am trying to add stuff to my slice but somehow the slice is never updated.
endpointsList := make([]string, 3)
for _, route := range routes {
if len(route.Endpoints) > 0 {
waitGroup.Add(1)
go endpointRoutine(route, template, route.Protected, &waitGroup, &endpointsList)
}
}
I pass the endpointsList by reference, meaning I should be able to assign new things to its memory location I think.
In the function endpointRoutine I do this:
list := make([]string, 3)
for _, r := range route.Endpoints {
list = append(list, "some data comes here...")
}
endpointsList = &list
When I do a printr after this (below my first bit of code and AFTER the waitGroup.Wait() part) the slice is still empty.
Obviously, I am overwriting the slice now and my final goal is to ADD to the slice. But when I try to add with this code:
endpointsList = append(endpointsList, "fdssdfsdfsdf")
It gives me the error:
cannot use endpointsList (type *[]string) as []Type
Can someone please explain to me what might be wrong?
With endpointsList = &list, you are assigning the pointer pointing to the slice to some other slice. To set the slice, do this instead:
*endpointsList=list

Why not just assign value to target in method header.clone

the official version
my question is What's the role of "sv"
func (h Header) Clone() Header {
if h == nil {
return nil
}
// Find total number of values.
nv := 0
for _, vv := range h {
nv += len(vv)
}
sv := make([]string, nv) // shared backing array for headers' values
h2 := make(Header, len(h))
for k, vv := range h {
n := copy(sv, vv)
h2[k] = sv[:n:n]
sv = sv[n:]
}
return h2
}
Why not just write it like this
just assgin value to h2 instead of creating a slice
for k, vv := range h {
// changed here
h2[k] = vv
}
It looks like Header is a map[string][]string, (maybe http.Header?). If it did what you suggested, then the new Header would be a shallow copy of the map, containing the original slices from the source Header. If the contents of the backing array of those slices are modified, the copied header would be modified as well. For instance:
s:=make([]string,0,10)
s=append(s,"a")
header[key]=s
h2:=header.Clone()
s=append(s,"b")
// Here, both header[key] and h2[key] has two elements, "a" and "b"
The prevent this, Clone is doing a deep copy, where each []string is also copied. That would need len(h) string slice allocations, one for each key. Instead, this algorithm uses one allocation, a single shared string slice containing all the strings of the original header.
The algorithm first counts the number of strings contained in the header, and allocates a string slice of that size. In the second for loop, it copies the strings from the value of a key to the shared string slice, creates a slice using that shared slice array and sets it as the value of the key, and then updates the shared slice to point to the next available empty slot. In the end, it is a deep-copy with a single memory allocation instead of len(h).

Adding anonymous struct element to slice

let's say I have a slice of anonymous structs
data := []struct{a string, b string}{}
Now, I would like to append a new item to this slice.
data = append(data, ???)
How do I do that? Any ideas?
Since you're using an anonymous struct, you have to again use an anonymous struct, with identical declaration, in the append statement:
data = append(data, struct{a string, b string}{a: "foo", b: "bar"})
Much easier would be to use a named type:
type myStruct struct {
a string
b string
}
data := []myStruct{}
data = append(data, myStruct{a: "foo", b: "bar"})
Actually, I found a way to add elements to array without repeated type declaration.
But it is dirty.
slice := []struct {
v, p string
}{{}} // here we init first element to copy it later
el := slice[0]
el2 := el // here we copy this element
el2.p = "1" // and fill it with data
el2.v = "2"
// repeat - copy el as match as you want
slice = append(slice[1:], el2 /* el3, el4 ...*/) // skip first, fake, element and add actual
Slice of pointers to struct is more conventional. At that case coping will slightly differ
slice := []*struct { ... }{{}}
el := slice[0]
el2 := *el
All this is far from any good practices. Use carefully.

Resources