Getting and setting length of arbitrary slice - go

To get length of any slice, I use reflect.ValueOf(slice).Len().
To set length of any slice, I use reflect.ValueOf(&slice).Elem().SetLen(n).
I have a field of type reflect.Value in my struct, and the value is set to be reflect.ValueOf(&slice) so that I can change the slice. But now I can't get the length of the underlying slice.
It would panic because of call of reflect.Value.Len on ptr Value if I call Len() directly, and call of reflect.Value.Len on interface Value if I call Elem().Len().
Below is the function I was trying to implement:
func pop(slice interface{}) interface{} {
v := reflect.ValueOf(slice)
length := v.Len()
last := v.Index(length - 1)
v.SetLen(length - 1)
return last
}
How can I do both with refect.Value of the slice pointer?

Write the function to work with a pointer to slice argument.
// pop removes and returns the last element from
// the slice pointed to by slicep.
func pop(slicep interface{}) interface{} {
v := reflect.ValueOf(slicep).Elem()
length := v.Len()
last := v.Index(length - 1)
v.SetLen(length - 1)
return last
}
Call it like this:
slice := []int{1, 2, 3}
last := pop(&slice)
fmt.Println(last) // prints 3
fmt.Println(slice) // prints [1 2]
Run it on the playground

Related

How to implement remove range on string array golang?

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]

Slice out of bounds using [][]int but works with map[int][]int

Why does this code work
graph := make(map[int][]int, 0)
graph[0] = append(graph[0], 1)
But if you replace first line with graph := make([][]int, 0) I get panic: runtime error: index out of range? It's very weird.
Lets simplify your code, to make it more obvious what's happening (Playground link):
graph1 := make(map[int]int, 0)
graph2 := make([]int, 0)
x := graph1[0] // Success
y := graph2[0] // Panic
From this we see that the difference is due to map[int] vs []int -- the second array in your type is actually irrelevant.
Now to understand why this is happening, we need to understand how Go handles map and slice access. From Go Maps in Action we learn:
If the requested key doesn't exist, we get the value type's zero value.
In your original code, the zero value for a slice ([]int), is nil, and append() treats nil as the first argument as an empty slice.
But when we try to access the 0th element of an empty slice, we get a panic, because the slice is empty.
In summary, append and the second slice of your type are both red herrings in your question. The panic happens when trying to access the non-existent element in the first dimension of your slice.
When you do make in graph := make(map[int][]int, 0), you are allocating memory to your map, not to array. So you might do this only
graph := make(map[int][]int).
Decomping you code:
type a []int
type m map[int]a
func main() {
fmt.Println("Hello, playground")
//decomping graph := make(map[int][]int, 0)
graph := make(m)
//map is empty
fmt.Println(len(graph))
//decomping graph[0] := append(graph[0], 1)
itm_a := 1
arr_a := []int{}
//appeding item to "a" type
arr_a = append(arr_a, itm_a)
//appending array of a to graph
graph[0] = arr_a
//print graph
fmt.Println(graph)
}
See in playground.
The error that you are getting is caused for conceptual error.
When you does graph := make([][]int, 0), you are allocation memory to a slice of slice, not an array. See in https://blog.golang.org/go-slices-usage-and-internals.
So you can does this (decomping solution):
type a []int
type m []a
func main() {
fmt.Println("Hello, playground")
//decomping graph := make([][]int, 0)
//see that you must be set the length
graph := make(m, 0)
//map is empty
fmt.Println(len(graph))
//this is incorrect: graph[0] := append(graph[0], 1)
//this is correct: graph[0] := append(graph[0], []int{1})
//see:
itm_a := 1
arr_a := []int{}
//appeding item to "a" type
arr_a = append(arr_a, itm_a)
//appending slice of a to graph (slice)
graph = append(graph, arr_a)
//print graph
fmt.Println(graph)
}
See in playground
The code panics due to slice length being 0. If you want to append anything to the slice, you just have to provide its length as below.
graph := make([][]int, 1)
fmt.Println(len(graph))
graph[0] = append(graph[0], 1)
fmt.Println(graph)
To append data to a slice at first level, append to its first index and then append to second level just like below.
graph := make([][]int, 0)
fmt.Println(len(graph))
graph = append(graph, []int{1})
Check Playground example
make(map[int][]int, 0) creates a map of []int.
By design in Go you can get any element from a map. And if it doesn't exist you receive the "zero" value which here is an empty slice.
graph := make(map[int][]int)
graph[4] = append(graph[4], 1)
graph[7] = append([]int{}, 1, 2)
graph[11] = append([]int{1, 2, 3}, 4, 5)
printing it gives this slice:
fmt.Printf("%#v\n", graph)
map[int][]int{
4:[]int{1},
7:[]int{1, 2},
11:[]int{1, 2, 3, 4, 5},
}
Your second example create an empty slice of []int slices. Slices work differently from maps, so indexing an element that doesn't exist will give you a panic.

The zero value of a slice is not nil

I was following the example https://tour.golang.org/moretypes/10
I modified the code expecting to get the same result. I did not. Is this a bug, or a documentation error? The tour states
A nil slice has a length and capacity of 0.
My y variable has a length and capacity of 0.
package main
import "fmt"
func myPrint(z []int) {
fmt.Println(z, len(z), cap(z))
if z == nil {
fmt.Println("nil!")
}
}
func main() {
var z []int
y := []int {}
myPrint(z)
myPrint(y)
}
Here is my output.
[] 0 0
nil!
[] 0 0
I was expecting a second "nil"~ Why didn't I get it?
nil Vs empty slice
If we think of a slice like this:
[pointer] [length] [capacity]
then:
nil slice: [nil][0][0]
empty slice: [addr][0][0] // it points to an address
From: "Go in action" book:
nil slice
They’re useful when you want to represent a slice that doesn’t exist, such as when an exception occurs in a function that returns a slice.
// Create a nil slice of integers.
var slice []int
empty slice
Empty slices are useful when you want to represent an empty collection, such as when a database query returns zero results.
// Use make to create an empty slice of integers.
slice := make([]int, 0)
// Use a slice literal to create an empty slice of integers.
slice := []int{}
👉 Regardless of whether you’re using a nil slice or an empty slice, the built-in functions append, len, and cap work the same.
Go playground example:
package main
import (
"fmt"
)
func main() {
var nil_slice []int
var empty_slice = []int{}
fmt.Println(nil_slice == nil, len(nil_slice), cap(nil_slice))
fmt.Println(empty_slice == nil, len(empty_slice), cap(empty_slice))
}
prints:
true 0 0
false 0 0
The doc you referenced states that a nil slice has a length and capacity of 0, but not that every slice of length and capacity of zero is a nil slice. The specification only says that the value of an uninitialized slice is nil.
This is a convenience to support len and cap on slices which are uninitialised (nil). Otherwise we would need to check for non-nil first in order to avoid panic. (This also holds for other in-built types like maps or channels.)
In terms of the fmt.Print output, the difference in behaviour is similar to printing an uninitialised (nil) pointer vs pointer to an empty structure:
var s *struct{} // uninitialised pointer
fmt.Println(s) // <nil>
s = &struct{}{} // pointer to an empty structure
fmt.Println(s) // &{}
In this case:
var z []int
You have declared a variable z but you did not initialize it.
In this case:
y := []int {}
You declared it and initialized it, you set it to an empty slice. Writing the second expression the long way makes the difference between the two expressions more clear:
var y []int = []int {}
Your y variable isn't the zero value for a slice. It's allocated via an empty slice literal.
// both of these allocate a slice
y := []int{}
z := []int{1, 2, 3}
A nil slice has a length and capacity of 0 and has no underlying array.
var s []string => no underlying array
var s = []string => create a underlying array but his length is 0.

Declare slice or make slice?

In Go, what is the difference between var s []int and s := make([]int, 0)?
I find that both works, but which one is better?
Simple declaration
var s []int
does not allocate memory and s points to nil, while
s := make([]int, 0)
allocates memory and s points to memory to a slice with 0 elements.
Usually, the first one is more idiomatic if you don't know the exact size of your use case.
In addition to fabriziom's answer, you can see more examples at "Go Slices: usage and internals", where a use for []int is mentioned:
Since the zero value of a slice (nil) acts like a zero-length slice, you can declare a slice variable and then append to it in a loop:
// Filter returns a new slice holding only
// the elements of s that satisfy f()
func Filter(s []int, fn func(int) bool) []int {
var p []int // == nil
for _, v := range s {
if fn(v) {
p = append(p, v)
}
}
return p
}
It means that, to append to a slice, you don't have to allocate memory first: the nil slice p int[] is enough as a slice to add to.
Just found a difference. If you use
var list []MyObjects
and then you encode the output as JSON, you get null.
list := make([]MyObjects, 0)
results in [] as expected.
A bit more complete example (one more argument in .make()):
slice := make([]int, 2, 5)
fmt.Printf("length: %d - capacity %d - content: %d", len(slice), cap(slice), slice)
Out:
length: 2 - capacity 5 - content: [0 0]
Or with a dynamic type of slice:
slice := make([]interface{}, 2, 5)
fmt.Printf("length: %d - capacity %d - content: %d", len(slice), cap(slice), slice)
Out:
length: 2 - capacity 5 - content: [<nil> <nil>]

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

Categories

Resources