How to implement remove range on string array golang? - go

How can I implement RemoveRange method in golang? It is a method in C# as shown here
I want to implement RemoveRange method on my hashCode string array and return new modified array back if possible with those ranges remove.
func removeRange(hashCode []string, idx int, count int) []string {
var temp []string
for i, s := range hashCode {
fmt.Println(i, s)
// confuse here on what to do
}
return temp
}

Simply slice the slice up until idx, skip count elements and append the rest to the result of the first slicing:
func removeRange(hashCode []string, idx int, count int) []string {
return append(hashCode[:idx], hashCode[idx+count:]...)
}
Testing it:
s := []string{"0", "1", "2", "3", "4", "5"}
fmt.Println(s)
s = removeRange(s, 1, 2)
fmt.Println(s)
Which outputs (try it on the Go Playground):
[0 1 2 3 4 5]
[0 3 4 5]
Note: the above implementation does not check whether indices are valid (whether they are in range). If not, the code could panic. Add necessary checks if you need to.
Note #2: the above implementation modifies the elements of the passed slice, the returned slice will share the backing array of the parameter. If you want to avoid this, if you want to leave the input intact and allocate a new slice for the result, then do so:
func removeRange(hashCode []string, idx int, count int) []string {
result := make([]string, 0, len(hashCode)-count)
result = append(result, hashCode[:idx]...)
result = append(result, hashCode[idx+count:]...)
return result
}
Try this one on the Go Playground.

You don't need a method or function for this at all in golang. Go slices can be subsliced and appended in place, which is how you can quickly and easily remove subsets from any slice.
Say you want to remove 2 elements, starting at index 2, you'd simply write:
Sub := append(original [:2], original [4:]...)
Demo
How this works:
original[:2] creates a sub-slice starting at 0, with a length of 2 elements (so index 0 and 1)
append because to this first part, we want to add the rest of the slice, minus the range we want to skip/remove
original[4:] creates another sub-slice, this time starting at index 4, and ending wherever original ends. Just like we don't explicitly mention 0 as the starting point in the first sub-slice, by not specifying a number of elements here, golang will just include all of the remaining elements in the slice.
... because append is a variadic function (built-in, but you get the point), we need to pass in every element we want to append as a new argument. The ... operator expands the sub-slice and passes in every element as a separate argument.
Because we assigned the new slice to a new variable, original will remain unchanged, so if you want to overwrite the slice, you just assign it to the same variable.
Note I wrote this on my phone, so markup and code may not be quite right, but this should answer your question at least

I've explained the code using // comments and if not commented, code is self explanatory.
package main
import (
"fmt"
"os"
)
func RemoveRange(s []string, index, count int) []string {
sLen := len(s)
// Similar semantics to match (similar) the behavior of
// C# implementation
switch {
case index < 0, count < 0: // arguments are not valid
fmt.Fprintln(os.Stderr, "error: argument out of range error")
return s
case index+count-1 >= sLen: // range results in exceeding the limit
fmt.Fprintln(os.Stderr, "error: argument error")
return s
}
// Create a slice p and pre-allocate the size required
// to store the resultant slice after removing range.
// Result := s[:] -> s[:index] + s[index+count:]
// Remove := s[index:index+count-1]
p := make([]string, 0, sLen-count)
p = append(p, s[:index]...)
p = append(p, s[index+count:]...)
return p
}
func main() {
s := []string{"0", "1", "2", "3", "4", "5"}
fmt.Println(s)
r := RemoveRange(s, 1, 3)
fmt.Println(r)
}
Output:
[0 1 2 3 4 5]
[0 4 5]

Related

how to understand the following code about golang slice?

Recently I found some code that I can't understand, below is my code:
func subsetsWithDup(nums []int) [][]int {
if len(nums) == 0 {
return [][]int{[]int{}}
}
sort.Ints(nums)
result := [][]int{}
backtracking(nums, &result, []int{}, 0)
return result
}
func backtracking(nums []int, result *[][]int, tempList []int, start int) {
*result = append(*result, tempList)
for i := start; i < len(nums); i++ {
if i > start && nums[i] == nums[i-1] {
continue
}
tempList = append(tempList, nums[i])
backtracking(nums, result, tempList, i+1)
tempList = tempList[:len(tempList)-1:len(tempList)-1]
}
}
and another approach:
func subsetsWithDup(nums []int) [][]int {
sort.Ints(nums)
return subsets(nums, []int{}, [][]int{})
}
func subsets(nums []int, result []int, results [][]int) [][]int {
newR := make([]int, len(result))
copy(newR, result)
results = append(results, newR)
if len(nums) == 0 {return results}
for i := 0; i < len(nums); i++ {
if i > 0 && nums[i] == nums[i - 1] {continue}
result = append(result, nums[i])
results = subsets(nums[i + 1:], result, results)
result = result[:len(result) - 1]
}
return results
}
In the first approach, I use the following code :
tempList = tempList[:len(tempList)-1:len(tempList)-1]
it works, but if I change it to:
tempList = tempList[:len(tempList)-1]
it dose not work.In the second approach which use copy function also works. I want to know what happens behind the code, any help is appreciated, thanks.
In Go, slice is a pointer type to maintain information about underlying array, so change of the underlying array would cause changes of the slice value, which sometimes might be surprising.
The second part of the puzzle is that append modifies the underlying array if the cap of the slice is sufficient. Document:
The append built-in function appends elements to the end of a slice.
If it has sufficient capacity, the destination is resliced to
accommodate the new elements. If it does not, a new underlying array
will be allocated. Append returns the updated slice. It is therefore
necessary to store the result of append, often in the variable holding
the slice itself.
So in you failed attempt, tempList = append(tempList, nums[i]) will possibly change value of previously stored slices in result.
On the other hand, the second approach creates a new slice with new underlying array and copy to it explictly, so the error is avoided. The first approach is more subtle, as it use a full slice expressions: tempList[:len(tempList)-1:len(tempList)-1]. The code limits the new slice's cap so append would have to allocate a new underlying array each time instead of using the orignal one.
More about full slice expressions(spec):
For an array, pointer to array, or slice a (but not a string), the primary expression
a[low : high : max]
constructs a slice of the same type, and with the same length and elements as the simple slice expression a[low : high]. Additionally, it controls the resulting slice's capacity by setting it to max - low. Only the first index may be omitted; it defaults to 0. After slicing the array a
a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]
the slice t has type []int, length 2, capacity 4, and elements
t[0] == 2
t[1] == 3

What is the mechanism of using append to prepend in Go?

Suppose I have a slice slice of type int. While declaring, I set the third argument to size, which I believe reserves memory for at least size ints by setting the cap parameter of the slice.
slice:=make([]int,0,size)
Now, suppose I have an integer variable value. To add it to the slice at the end, I use
slice=append(slice,value)
If the number of elements currently in the slice is less than size, then there will be no need to copy the entire underlying array to a new location in order to add the new element.
Further, if I want to prepend value to slice, as suggested here and here, I use
slice=append([]int{value},slice...)
My question is, what happens in this case? If the number of elements is still less than size, how are the elements stored in the memory? Assuming a contiguous allocation when the make() function was invoked, are all existing elements right shifted to free the first space for value? Or is memory reallocated and all elements copied?
The reason for asking is that I would like my program to be as fast as possible, and would like to know if this is a possible cause for slowing it down. If it is so, is there any alternative way of prepending that would be more time efficient?
With reslicing and copying
The builtin append() always appends elements to a slice. You cannot use it (alone) to prepend elements.
Having said that, if you have a slice capacity bigger than length (has "free" space after its elements) to which you want to prepend an element, you may reslice the original slice, copy all elements to an index one higher to make room for the new element, then set the element to the 0th index. This will require no new allocation. This is how it could look like:
func prepend(dest []int, value int) []int {
if cap(dest) > len(dest) {
dest = dest[:len(dest)+1]
copy(dest[1:], dest)
dest[0] = value
return dest
}
// No room, new slice need to be allocated:
// Use some extra space for future:
res := make([]int, len(dest)+1, len(dest)+5)
res[0] = value
copy(res[1:], dest)
return res
}
Testing it:
s := make([]int, 0, 5)
s = append(s, 1, 2, 3, 4)
fmt.Println(s)
s = prepend(s, 9)
fmt.Println(s)
s = prepend(s, 8)
fmt.Println(s)
Output (try it on the Go Playground):
[1 2 3 4]
[9 1 2 3 4]
[8 9 1 2 3 4]
Note: if no room for the new element, since performance does matter now, we didn't just do:
res := append([]int{value}, dest...)
Because it does more allocations and copying than needed: allocates a slice for the literal ([]int{value}), then append() allocates a new when appending dest to it.
Instead our solution allocates just one new array (by make(), even reserving some space for future growth), then just set value as the first element and copy dest (the previous elements).
With linked list
If you need to prepend many times, a normal slice may not be the right choice. A faster alternative would be to use a linked list, to which prepending an element requires no allocations of slices / arrays and copying, you just create a new node element, and you designate it to be the root by pointing it to the old root (first element).
The standard library provides a general implementation in the container/list package.
With manually managing a larger backing array
Sticking to normal slices and arrays, there is another solution.
If you're willing to manage a larger backing array (or slice) yourself, you can do so by leaving free space before the slice you use. When prepending, you can create a new slice value from the backing larger array or slice which starts at an index that leaves room for 1 element to be prepended.
Without completeness, just for demonstration:
var backing = make([]int, 15) // 15 elements
var start int
func prepend(dest []int, value int) []int {
if start == 0 {
// No more room for new value, must allocate bigger backing array:
newbacking := make([]int, len(backing)+5)
start = 5
copy(newbacking[5:], backing)
backing = newbacking
}
start--
dest = backing[start : start+len(dest)+1]
dest[0] = value
return dest
}
Testing / using it:
start = 5
s := backing[start:start] // empty slice, starting at idx=5
s = append(s, 1, 2, 3, 4)
fmt.Println(s)
s = prepend(s, 9)
fmt.Println(s)
s = prepend(s, 8)
fmt.Println(s)
// Prepend more to test reallocation:
for i := 10; i < 15; i++ {
s = prepend(s, i)
}
fmt.Println(s)
Output (try it on the Go Playground):
[1 2 3 4]
[9 1 2 3 4]
[8 9 1 2 3 4]
[14 13 12 11 10 8 9 1 2 3 4]
Analysis: this solution makes no allocations and no copying when there is room in the backing slice to prepend the value! All that happens is it creates a new slice from the backing slice that covers the destination +1 space for the value to be prepended, sets it and returns this slice value. You can't really get better than this.
If there is no room, then it allocates a larger backing slice, copies over the content of the old, and then does the "normal" prepending.
With tricky slice usage
Idea: imagine that you always store elements in a slice in backward order.
Storing your elements in backward order in a slice means a prepand becomes append!
So to "prepand" an element, you can simply use append(s, value). And that's all.
Yes, this has its limited uses (e.g. append to a slice stored in reverse order has the same issues and complexity as a "normal" slice and prepand operation), and you lose many conveniences (ability to list elements using for range just to name one), but performance wise nothing beats prepanding a value just by using append().
Note: iterating over the elements that stores elements in backward order has to use a downward loop, e.g.:
for i := len(s) - 1; i >= 0; i-- {
// do something with s[i]
}
Final note: all these solutions can easily be extended to prepend a slice instead of just a value. Generally the additional space when reslicing is not +1 but +len(values), and not simply setting dst[0] = value but instead a call to copy(dst, values).
The "prepend" call will need to allocate an array and copy all elements because a slice in Go is defined as a starting point, a size and an allocation (with the allocation being counted from the starting point).
There is no way a slice can know that the element before the first one can be used to extend the slice.
What will happen with
slice = append([]int{value}, slice...)
is
a new array of a single element value is allocated (probably on stack)
a slice is created to map this element (start=0, size=1, alloc=1)
the append call is done
append sees that there is not enough room to extend the single-element slice so allocates a new array and copies all the elements
a new slice object is created to refer to this array
If appending/removing at both ends of a large container is the common use case for your application then you need a deque container. It is unfortunately unavailable in Go and impossible to write efficiently for generic contained types while maintaining usability (because Go still lacks generics).
You can however implement a deque for your specific case and this is easy (for example if you have a large container with a known upper bound may be a circular buffer is all you need and that is just a couple of lines of code away).
I'm very new to Go, so may be the following is very bad Go code... but it's an attempt to implement a deque using a growing circular buffer (depending on the use case this may be or may be not a good solution)
type Deque struct {
buffer []interface{}
f, b, n int
}
func (d *Deque) resize() {
new_buffer := make([]interface{}, 2*(1+d.n))
j := d.f
for i := 0; i < d.n; i++ {
new_buffer[i] = d.buffer[j]
d.buffer[j] = nil
j++
if j == len(d.buffer) {
j = 0
}
}
d.f = 0
d.b = d.n
d.buffer = new_buffer
}
func (d *Deque) push_back(x interface{}) {
if d.n == len(d.buffer) {
d.resize()
}
d.buffer[d.b] = x
d.b++
if d.b == len(d.buffer) {
d.b = 0
}
d.n++
}
func (d *Deque) push_front(x interface{}) {
if d.n == len(d.buffer) {
d.resize()
}
if d.f == 0 {
d.f = len(d.buffer)
}
d.f--
d.buffer[d.f] = x
d.n++
}
func (d *Deque) pop_back() interface{} {
if d.n == 0 {
panic("Cannot pop from an empty deque")
}
if d.b == 0 {
d.b = len(d.buffer)
}
d.b--
x := d.buffer[d.b]
d.buffer[d.b] = nil
d.n--
return x
}
func (d *Deque) pop_front() interface{} {
if d.n == 0 {
panic("Cannot pop from an empty deque")
}
x := d.buffer[d.f]
d.buffer[d.f] = nil
d.f++
if d.f == len(d.buffer) {
d.f = 0
}
d.n--
return x
}

Do Clearing slices in golang guarantees garbage collection?

I wanted to implement time based slots for holding data using golang slices. I managed to come up with a go program like this and it also works. But I have few questions regarding garbage collection and the general performance of this program. Does this program guarantee garbage collection of items once slice is equated to nil? And while shuffling slices, I hope this program does not do any deep copying.
type DataSlots struct {
slotDuration int //in milliseconds
slots [][]interface{}
totalDuration int //in milliseconds
}
func New(slotDur int, totalDur int) *DataSlots {
dat := &DataSlots{slotDuration: slotDur,
totalDuration: totalDur}
n := totalDur / slotDur
dat.slots = make([][]interface{}, n)
for i := 0; i < n; i++ {
dat.slots[i] = make([]interface{}, 0)
}
go dat.manageSlots()
return dat
}
func (self *DataSlots) addData(data interface{}) {
self.slots[0] = append(self.slots[0], data)
}
// This should be a go routine
func (self *DataSlots) manageSlots() {
n := self.totalDuration / self.slotDuration
for {
time.Sleep(time.Duration(self.slotDuration) * time.Millisecond)
for i := n - 1; i > 0; i-- {
self.slots[i] = self.slots[i-1]
}
self.slots[0] = nil
}
}
I removed critical section handling in this snippet to make it concise.
Once your slice is set too nil, any values contained in the slice are available for garbage collection, provided that the underlying array isn't shared with another slice.
Since there are no slice operations in your program, you never have multiple references to the same array, nor are you leaving data in any inaccessible portions of the underlying array.
What you need to be careful of, is when you're using slice operations:
a := []int{1, 2, 3, 4}
b := a[1:3]
a = nil
// the values 1 and 4 can't be collected, because they are
// still contained in b's underlying array
c := []int{1, 2, 3, 4}
c = append(c[1:2], 5)
// c is now []int{2, 5}, but again the values 1 and 4 are
// still in the underlying array. The 4 may be overwritten
// by a later append, but the 1 is inaccessible and won't
// be collected until the underlying array is copied.
While append does copy values when the capacity of the slice in insufficient, only the values contained in the slice are copied. There is no deep copy of any of the values.

Golang: Problems when using append on slice

I am using golang. Here is my code:
func main() {
quanPailie([]int{1, 2})
}
func quanPailie(nums []int) [][]int {
COUNT := len(nums)
//only one item
if COUNT == 1 {
return [][]int{nums}
}
insertItem(quanPailie(nums[:COUNT-1]), nums[COUNT-1])
return [][]int{}
}
func insertItem(res [][]int, insertNum int) {
fmt.Println("insertItem,res:", res, "insertNum", insertNum) //insertItem,res: [[1]] insertNum 2
for _, v := range res {
for i := 0; i < len(v); i++ {
fmt.Println("===before,v:", v)
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v)
fmt.Println("ccc", c)
}
}
}
What makes me very confused is the output:
===before,v: [1]
===after,v: [2]
Why did the value of v change? Hope someone can help me. Thanks a lot.
Go playground: https://play.golang.org/p/wITYsGpX7U
EDIT:
Thanks for icza's great help, I think I have understood this problem.
And, here is a simple code to show this issue.
func test1() {
nums := []int{1, 2, 3}
_ = append(nums[:2], 4)
fmt.Println("test1:", nums)
//nums changes because the cap is big enought, the original array is modified.
}
func test2() {
nums := []int{1, 2, 3}
c := append(nums[:2], []int{4, 5, 6}...)
fmt.Println("test2:", nums)
fmt.Println("cc:", c)
//nums dont't change because the cap isn't big enought.
//a new array is allocated while the nums still points to the old array.
//Of course, the return value of append points to the new array.
}
Go playground: https://play.golang.org/p/jBNFsCqUn3
This is the code in question:
fmt.Println("===before,v:", v)
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v)
You ask why v changes between the 2 Println() statements.
Because you are using the builtin append() function, quoting from its doc:
The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice.
So if the slice you append to has enough room (capacity) to accomodate the elements you want to append, no new slice will be allocated, instead the destination slice will be re-sliced (which will use the same underlying array) and append will happen in that.
Let's check the capacity:
fmt.Println("===before,v:", v, cap(v))
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v, cap(v))
Output:
===before,v: [1] 2
===after,v: [2] 2
The v slice has a capacity of 2. When for loop starts, i=0, v[:i] is v[:0] which is an empty slice (but has capacity 2) and so appending 1 or 2 elements will not allocate a new array/slice, it will be done "in place". This "in place" is the 0th element of v, since v[:i] is shorthand for v[0:i]. Hence the elements will be appended starting from v[0] in the underlying array which is shared, so the element denoted by v[0] will change.
Note that slicing a slice results in a slice which shares its underlying backing array with the original slice (does not make a copy of the elements).
If you want to avoid this, use or allocate a new slice, copy original content and append to the new slice, e.g.:
src := []int{1, 2}
c := make([]int, len(src))
copy(c, src)
// Append something:
c = append(c, 3, 4)
fmt.Println(src) // [1 2] - src doesn't change
fmt.Println(c) // [1 2 3 4]

Inserting Missing value NOT working GoLang

I'm trying to Insert a Int value to slice if it is missing in that.
My Code :
package main
import (
"fmt"
)
func AppendIfMissing(slice []int, i int) []int {
for _, ele := range slice {
if ele == i {
fmt.Println(i)
return slice
}
}
fmt.Println("i value is ", i)
slice = append(slice, i)
return slice
}
func main() {
slice1 := []int{1, 2, 3, 4}
AppendIfMissing(slice1, 60)
fmt.Println("slice after adding :", slice1)
}
OutPut :
i value is 60
slice after adding : [1 2 3 4]
Appending to slice is not happening.What is wrong with my code ?
AppendIfMissing returns a slice which you need to affect to a variable.
append(slice, i) creates a new slice, which means the parameter slice isn't modified, it refers to a all new slice:
which is returned at the end
which needs to be affected to a variable
slice1 = AppendIfMissing(slice1, 60)
See go playground example.
I agree that the article "Arrays, slices (and strings): The mechanics of 'append'" mentions
Even though the slice header is passed by value, the header includes a pointer to elements of an array, so both the original slice header and the copy of the header passed to the function describe the same array.
Therefore, when the function returns, the modified elements can be seen through the original slice variable.
But the function in that article wasn't using append:
func AddOneToEachElement(slice []byte) {
for i := range slice {
slice[i]++
}
}
the contents of a slice argument can be modified by a function, but its header cannot
And by doing
slice = append(slice, i)
you modify the header, you reallocate the resulting slice to a completely different array.
That won't be visible outside of the function.
More generic version of AppendIfMissing, it requires go version >= 1.18
func AppendIfMissing[T comparable](slice []T, i T) []T {
for _, ele := range slice {
if ele == i {
return slice
}
}
return append(slice, i)
}

Resources