Remove an interface item from a slice - go

I want to remove an item in a slice w/o having to use a specific function for every type of items in the slice. So, I am using interface{} as the slice item type:
package main
import "fmt"
func sliceRemoveItem(slice []interface{}, s int) []interface{} {
return append(slice[:s], slice[s+1:]...)
}
func main() {
array := []int{1,2,3,4,5,6,7}
fmt.Println(array)
fmt.Println(sliceRemoveItem(array,1))
}
But goLang doesn't like it:
./prog.go:13:30: cannot use array (type []int) as type []interface {} in argument to sliceRemoveItem
https://play.golang.org/p/wUrR5iGRZ5Y
Any idea how to do this? Is it possible to use a generic single function accepting any type of slice items?
Refs: How to delete an element from a Slice in Golang

You're trying to pass a slice of int as a slice of interface{}. Go doesn't do this conversion implicitly since it is a costly operation.
Check out this answer: https://stackoverflow.com/a/12754757
You can either accept []interface{}, but do the conversion explicitly, or specify the type as []int. This works:
package main
import "fmt"
func sliceRemoveItem(slice []int, s int) []int {
return append(slice[:s], slice[s+1:]...)
}
func main() {
array := []int{1,2,3,4,5,6,7}
fmt.Println(array)
fmt.Println(sliceRemoveItem(array,1))
}

Use the reflect package.
// sliceRemoveItem removes item at index i from the
// slice pointed to by slicep.
func sliceRemoveItem(slicep interface{}, i int) {
v := reflect.ValueOf(slicep).Elem()
v.Set(reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())))
}
Call it like this:
slice := []int{1, 2, 3, 4, 5, 6, 7}
sliceRemoveItem(&slice, 1)
To avoid type assertions in the caller, the function uses a pointer to slice argument.
Run it on the Go playground

Related

How to add new elements of a slice automatically to function parameter as slice grows

Is there a way to doing this automatically ?
package main
import "fmt"
func main() {
var a []string
a = append(a, "this", "this2", "this3")
increaseArguments(a)
a = append(a, "this4")
increaseArguments(a)
}
func increaseArguments(b []string) {
// I want, when i add new element to slice i want this function act as this
// fmt.Println(b[0],b[1], b[2], b[3])
fmt.Println(b[0], b[1], b[2])
}
Instead of adding b[3] as argument to fmt.Println is there a way to add it automatically ?
Note that if b would be of type []any, you could pass it as the value of the variadic parameter of fmt.Println():
fmt.Println(b...)
But since b is of type []string, you can't.
But if you transform b into a []any slice, you can. You can use this helper function to do it:
func convert[T any](x []T) []any {
r := make([]any, len(x))
for i, v := range x {
r[i] = v
}
return r
}
And then:
func increaseArguments(b []string) {
fmt.Println(convert(b)...)
}
This will output (try it on the Go Playground):
this this2 this3
this this2 this3 this4
Note: creating a new slice in convert() will not make this solution any slower, because passing values explicitly (like fmt.Println(b[0], b[1], b[2])) also implicitly creates a slice.
See related question: How to pass multiple return values to a variadic function?

How to make variable type slices

I was learning go by doing some of the problems I have already completed in other languages.
So one of the problem was:-
Find the sum of the elements in Array
Arr = [1,2,[7,8,3],1,[3,[2,[4,5]],6]]
I have tried making an interface type array but that also didn't work
func ProdSum(prods interface{},sum int ,depth int){
for id,val:=range prods{
if isArray(val){
ProdSum(val,sum,depth)
}
}
}
type arr []interface{}
func main(){
arra:=arr{5,2,arr{7,-1},3,arr{6,arr{-13,8},4}}
ProdSum(arra,0,1)
}
Error :
./prodsum.go:16:14: cannot range over prods (type interface {})
Use a type assertion or type switch to determine if a value is a slice or an integer. Here's how to do it using a type switch:
func sum(value interface{}) int {
switch value := value.(type) {
case arr:
var result int
for _, v := range value {
result += sum(v)
}
return result
case int:
return value
default:
panic("type not handled")
}
}
Call it like this:
a := arr{5, 2, arr{7, -1}, 3, arr{6, arr{-13, 8}, 4}}
n := sum(a)
Run it on the playground.
The way you are declaring and initializing the array is correct, although you don't need the arr type.
Without the arr type it will look like this:
Running in Go Playground
myArr := []interface{}{1, 2, []int{7, 8, 3}, 1, []interface{}{3, []interface{}{2, []int{4, 5}}, 6}}
Now, the specific error you are seeing is not related to your slice declaration. It is related to the function signature of ProdSum.
ProdSum takes an argument of type interface{}- which is not a slice so you cannot iterate over it. You want the argument to have a type of []interface{} (slice of empty interface):
func ProdSum(prods []interface{}, sum int, depth int) {
for itr, prod := range prods {
// prod has type: interface{}
}
}
Keep in mind that you will need to do type assertions when reading the values out of the slice and summing them. You can't add an interface{} to anything because (before an assertion to int) Go doesn't recognize it as a number.

How to write a pop() function

a := []int{1,2,3}
x, a := a[len(a)-1], a[:len(a)-1]
fmt.Println(a,x)
How to create a pop() function that will do the same for any type of an array?
Here is what I came up with so far:
func pop(a []*interface{}) interface{}{
x := a[len(a)-1]
a = a[:len(a)-1]
return x
}
func main(){
a := []int{1,2,3}
x = pop(a)
fmt.Println(a,x) // -> [1,2] 3
}
But I get cannot use a (type []int) as type []interface {}or other error messages if I try to tweak the code by trial and error.
package main
import (
"fmt"
"reflect"
)
func pop(a interface{}) interface{} {
v := reflect.ValueOf(a).Elem()
x := v.Index(v.Len() - 1)
v.SetLen(v.Len() - 1)
return x
}
func main() {
a := []int{1, 2, 3}
x := pop(&a)
fmt.Println(a, x) // -> [1,2] 3
}
Though this can be implemented, I still think that x, a = a[len(a)-1], a[:len(a)-1] should be better than a pop function.
The go type system doesn't allow you to cast from []type1 -> []type2. Even if it did interfaces are a struct containing a type id and pointer to the object, where normally you would just have the object. Because of this you need to take a interface{} and use reflect to do the slicing.
func pop(slice interface{}) (interface{}, interface{}) {
v := reflect.ValueOf(slice)
return v.Slice(0,v.Len()-1).Interface(), v.Index(v.Len()-1).Interface()
}
Go Playground
Note that this loses compile time type safety, because it must use an interface. Additionally, due to using interfaces the poped value may be allocated, creating extra GC pressure.
Common Go style typically recommends not writing a function like this, and just inlining the small amount of code manually.
After all that really good anwers using reflection I also want to add one answer which offers a more idiomatic Go solution. Like Rob Pike said in his great talk about Go Proverbs
interface{} says nothing
Reflection is never clear
So there should be also one answer showing the idiomatic Go way. This solution does not work for slices of standard types. But there the answer of cshu shows the best solution: x, a = a[len(a)-1], a[:len(a)-1]
For own defined types we have to define a Poper interface and the Pop function takes that as input and returns an empty interface.
type Poper interface {
Pop() interface{}
}
type MyType struct {
a []int
}
func (mt *MyType) Pop() interface{} {
x := mt.a[len(mt.a)-1]
mt.a = mt.a[:len(mt.a)-1]
return x
}
func Pop(p Poper) interface{} {
return p.Pop()
}
func main() {
a := &MyType{[]int{1, 2, 3}}
fmt.Println(Pop(a), a)
}
https://play.golang.org/p/UbDkoVYSMA
At all it is not a good idea to return an empty interface, because all following code has to support the interface{}.
The following code example does not work:
func main() {
a := &MyType{[]int{1, 2, 3}}
fmt.Println(Pop(a), a)
var b int
b = Pop(a)
}
https://play.golang.org/p/wg9__O44A8
The error says everything about that problem: cannot use Pop(a) (type interface {}) as type int in assignment: need type assertion
So the Pop() function does work by returning interface{} but the rest of the code using the result of that function needs to make a type assertion. So if you can avoid it you should search for another solution using types.

Why are Slices insides structs "passed by reference" when passed into functions in Go?

package main
import "fmt"
func main() {
a := SomeType{myslice: []int{1, 2, 3}, decimal: 2.33}
for _, i := range a.myslice {
fmt.Println(i)
}
fmt.Println(a.decimal)
addOne(a)
for _, i := range a.myslice {
fmt.Println(i)
}
fmt.Println(a.decimal)
}
type SomeType struct {
myslice []int
decimal float32
}
func addOne(s SomeType) {
s.myslice[0]++
s.decimal += 1.2
}
The output for the code above is:
1
2
3
2.33
2
2
3
2.33
Even though i have not passed the SomeType object a by reference the myslice field is being modified in the original object. Why is this happening? Is there anyway to pass the entire object by value without having to create a copy of the original object?
The slice is not really being passed by reference; if you append to it in addOne, its length will not change. But a slice contains a reference (or pointer) to its backing array. So when you copy a slice, the new one shares the same backing array with the old one.
The fact that the slice is inside a struct doesn't make any difference. You would see the same thing if you changed addOne to just take a slice instead of the whole struct.

How to remove an item from a slice by calling a method on the slice

Go has stumped me again. Hopefully someone can help. I've created a slice (mySlice) that contains pointers to structs (myStruct).
The problem is the "Remove" method. When we're inside "Remove" everything is fine, but once we return, the slice size hasn't changed, and so we see the last element listed twice.
I originally tried writing "Remove" using the same pattern used in the "Add" method, but it wouldn't compile and has been commented out.
I can get it to work by returning the newly created slice to the calling function, but I don't want to do this because mySlice (ms) is a singleton.
And if I hadn't asked enough already...
The code for the "Add" method is working, although I'm not sure how. From what I can gather "Add" is receiving a pointer to the slice header (the 3 item "struct"). From what I've read, the length and capacity of an slice don't get passed to methods (when passing by value), so perhaps passing a pointer to the slice allows the method to see and use the length and capacity thereby allowing us to "append". If this is true, then why doesn't the same pattern work in "Remove"?
Thanks very much for everyone's insights and help!
package main
import (
"fmt"
)
type myStruct struct {
a int
}
type mySlice []*myStruct
func (slc *mySlice) Add(str *myStruct) {
*slc = append(*slc, str)
}
//does not compile with reason: cannot slice slc (type *mySlice)
//func (slc *mySlice) Remove1(item int) {
// *slc = append(*slc[:item], *slc[item+1:]...)
//}
func (slc mySlice) Remove(item int) {
slc = append(slc[:item], slc[item+1:]...)
fmt.Printf("Inside Remove = %s\n", slc)
}
func main() {
ms := make(mySlice, 0)
ms.Add(&myStruct{0})
ms.Add(&myStruct{1})
ms.Add(&myStruct{2})
fmt.Printf("Before Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms)
ms.Remove(1) //remove element 1 (which also has a value of 1)
fmt.Printf("After Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms)
}
and the results...
Before Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{1}) %!s(*main.myStruct=&{2})]
Inside Remove = [%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2})]
After Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2}) %!s(*main.myStruct=&{2})]
You were right the first time with Remove1(). Remove gets a copy of the slice and therefore cannot change the length of the slice.
The issue in your remove function is that according to order of operations in Go, slicing comes before dereferencing.
The fix is to change *slc = append(*slc[:item], *slc[item+1:]...) to *slc = append((*slc)[:item], (*slc)[item+1:]...).
However I would recommend the following for readability and maintainability:
func (slc *mySlice) Remove1(item int) {
s := *slc
s = append(s[:item], s[item+1:]...)
*slc = s
}
Because append would not necessarily return the same address of reference to the slice, as Stephen Weinberg has pointed out.
Another way to workaround with this limitation is defining a struct that wraps the slice.
for example:
package main
import "fmt"
type IntList struct {
intlist []int
}
func (il *IntList) Pop() {
if len(il.intlist) == 0 { return }
il.intlist = il.intlist[:len(il.intlist)-1]
}
func (il *IntList) Add(i... int) {
il.intlist = append(il.intlist, i...)
}
func (il *IntList) String() string {
return fmt.Sprintf("%#v",il.intlist)
}
func main() {
intlist := &IntList{[]int{1,2,3}}
fmt.Println(intlist)
intlist.Pop()
fmt.Println(intlist)
intlist.Add([]int{4,5,6}...)
fmt.Println(intlist)
}
output:
[]int{1, 2, 3}
[]int{1, 2}
[]int{1, 2, 4, 5, 6}

Resources