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

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

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.

Duplicate slice of pointers to structs with slightly different values

Given two nested types
type Inner struct {
InnerVal int
}
type Outer struct {
InnerStruct *Inner
OuterVal int
}
I need to duplicate a slice of pointers to Outer
originalSlice := []*Outer{<plenty_of_items>}
with itself, but having updated field values in the duplicates, including the Outer.InnerStruct.InnerVal.
To do so I create a new slice of the same type and length as originalSlice, append pointers to newly created structs with altered values to it, and finally append these items to the originalSlice
duplicateSlice := make([]*Outer, len(originalSlice))
for _, originalItem := range originalSlice {
duplicateSlice = append(duplicateSlice, &Outer{
InnerStruct: &Inner{
InnerVal: originalItem.InnerStruct.InnerVal + 1
},
OuterVal: originalItem.OuterVal + 1,
})
}
originalSlice = append(originalSlice, duplicateSlice...)
While this is verbose enough to follow the pointers around, or so I thought, when passed to a function right after as nowDoubledSlice, and accessed via loop
someOtherSlice := make([]*types.Inner, len(nowDoubledSlice))
for i, doubledItem := range nowDoubledSlice {
someOtherSlice[i] = doubledItem.InnerStruct
}
I get a
runtime error: invalid memory address or nil pointer dereference
Why is that? And is there a more elegant or idiomatic way to duplicate a slice of pointers to structs, while altering the duplicates' fields?
It's nothing to do with your pointer creation, it's your slice allocation. This line:
duplicateSlice := make([]*Outer, len(originalSlice))
Creates a new slice of length len(originalSlice), filled with zero-value elements. What you likely want instead is:
duplicateSlice := make([]*Outer, 0, len(originalSlice))
to create a slice of length 0 but capacity of len(originalSlice). This works fine, as you can see here.
Alternatively, you could keep make([]*Outer, len(originalSlice)) and use indexing instead of append in your loop:
for i, originalItem := range originalSlice {
duplicateSlice[i] =&Outer{
InnerStruct: &Inner{
InnerVal: originalItem.InnerStruct.InnerVal + 1,
},
OuterVal: originalItem.OuterVal + 1,
}
}
Which works just as well, as you can see here.

Optimal way to add or remove slice element in Go without broke elements order

Assume I have []struct{} and I need to know whether an element with id = A exists in the slice. If exists, the element will be removed or moved to index 0 according to request in user input. So, how to find an element in golang slice in optimal way without check each element? Or, is using slice.contains(obj) enough? Then, if the element exists, I will do action according to request in user input. If the request is remove, I will remove it without broke the elements order. But if the request is add, I will move the element to index 0.
Note: The function will be often called.
Thank you.
It is not difficult to write function to find element by iterating over slice:
func contains(s []your_struct, e int) (bool, int) {
for idx, a := range s {
if a.id == e {
return true, idx
}
}
return false, -1
}
If you a going to call the function often it may be useful to sort the slice by id field and implement binary search over slice of your_struct.
If the slice is not very big you can create additional data structure - map[int]int and keep the indexes of elements of the slice in this map. But in this case you need to synchronize content of your slice and the map when you are modifying one of them:
your_map := make(map[int]int)
if idx, ok := your_map[id]; ok {
// ...
}
If you need to check many times then
it's better to create a map[string]int of id field one time.
And every time just check map contains that id or not
Here,id as key and slice index as value
mp := make(map[string]int)
for idx, a := range yourStuctSlice {
mp[a.id] = idx
}
if idx, ok := mp[id]; ok {
// remove the element using idx
}
If new element added in slice then update the map also
mp[newElement.id] = true
If you want to remove searched element you can remove by slice index
func RemoveIndex(s []yourStuct, index int) []int {
return append(s[:index], s[index+1:]...)
}
if idx, ok := mp[id]; ok {
yourStuctSlice = RemoveIndex(yourStuctSlice , idx)
delete(mp , id); // Remove from map also for next search
}

Go: append directly to slice found in a map

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

Resources