Concatenate two slices in Go - go

I'm trying to combine the slice [1, 2] and the slice [3, 4]. How can I do this in Go?
I tried:
append([]int{1,2}, []int{3,4})
but got:
cannot use []int literal (type []int) as type int in append
However, the documentation seems to indicate this is possible, what am I missing?
slice = append(slice, anotherSlice...)

Add dots after the second slice:
// vvv
append([]int{1,2}, []int{3,4}...)
This is just like any other variadic function.
func foo(is ...int) {
for i := 0; i < len(is); i++ {
fmt.Println(is[i])
}
}
func main() {
foo([]int{9,8,7,6,5}...)
}

Appending to and copying slices
The variadic function append appends zero or more values x to s
of type S, which must be a slice type, and returns the resulting
slice, also of type S. The values x are passed to a parameter of
type ...T where T is the element type of S and the respective
parameter passing rules apply. As a special case, append also accepts
a first argument assignable to type []byte with a second argument of
string type followed by .... This form appends the bytes of the
string.
append(s S, x ...T) S // T is the element type of S
s0 := []int{0, 0}
s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
Passing arguments to ... parameters
If f is variadic with final parameter type ...T, then within the
function the argument is equivalent to a parameter of type []T. At
each call of f, the argument passed to the final parameter is a new
slice of type []T whose successive elements are the actual arguments,
which all must be assignable to the type T. The length of the slice is
therefore the number of arguments bound to the final parameter and may
differ for each call site.
The answer to your question is example s3 := append(s2, s0...) in the Go Programming Language Specification. For example,
s := append([]int{1, 2}, []int{3, 4}...)

Nothing against the other answers, but I found the brief explanation in the docs more easily understandable than the examples in them:
func append
func append(slice []Type, elems ...Type) []Type 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:
slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)
As a special case, it is legal to append a string to a byte slice,
like this:
slice = append([]byte("hello "), "world"...)

I would like to emphasize #icza answer and simplify it a bit since it is a crucial concept. I assume that reader is familiar with slices.
c := append(a, b...)
This is a valid answer to the question.
BUT if you need to use slices 'a' and 'c' later in code in different context, this is not the safe way to concatenate slices.
To explain, lets read the expression not in terms of slices, but in terms of underlying arrays:
"Take (underlying) array of 'a' and append elements from array 'b' to
it. If array 'a' has enough capacity to include all elements from 'b'
- underlying array of 'c' will not be a new array, it will actually be array 'a'. Basically, slice 'a' will show len(a) elements of
underlying array 'a', and slice 'c' will show len(c) of array 'a'."
append() does not necessarily create a new array! This can lead to unexpected results. See Go Playground example.
Always use make() function if you want to make sure that new array is allocated for the slice. For example here are few ugly but efficient enough options for the task.
la := len(a)
c := make([]int, la, la + len(b))
_ = copy(c, a)
c = append(c, b...)
la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)

I think it's important to point out and to know that if the destination slice (the slice you append to) has sufficient capacity, the append will happen "in-place", by reslicing the destination (reslicing to increase its length in order to be able to accommodate the appendable elements).
This means that if the destination was created by slicing a bigger array or slice which has additional elements beyond the length of the resulting slice, they may get overwritten.
To demonstrate, see this example:
a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)
x, y := a[:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))
x = append(x, y...)
fmt.Printf("x: %v\n", x)
fmt.Printf("a: %v\n", a)
Output (try it on the Go Playground):
a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 10
x: [1 2 3 4]
a: [1 2 3 4 0 0 0 0 0 0]
We created a "backing" array a with length 10. Then we create the x destination slice by slicing this a array, y slice is created using the composite literal []int{3, 4}. Now when we append y to x, the result is the expected [1 2 3 4], but what may be surprising is that the backing array a also changed, because capacity of x is 10 which is sufficient to append y to it, so x is resliced which will also use the same a backing array, and append() will copy elements of y into there.
If you want to avoid this, you may use a full slice expression which has the form
a[low : high : max]
which constructs a slice and also controls the resulting slice's capacity by setting it to max - low.
See the modified example (the only difference is that we create x like this: x = a[:2:2]:
a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)
x, y := a[:2:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))
x = append(x, y...)
fmt.Printf("x: %v\n", x)
fmt.Printf("a: %v\n", a)
Output (try it on the Go Playground)
a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 2
x: [1 2 3 4]
a: [1 2 0 0 0 0 0 0 0 0]
As you can see, we get the same x result but the backing array a did not change, because capacity of x was "only" 2 (thanks to the full slice expression a[:2:2]). So to do the append, a new backing array is allocated that can store the elements of both x and y, which is distinct from a.

append( ) function and spread operator
Two slices can be concatenated using append method in the standard golang library. Which is similar to the variadic function operation. So we need to use ...
package main
import (
"fmt"
)
func main() {
x := []int{1, 2, 3}
y := []int{4, 5, 6}
z := append([]int{}, append(x, y...)...)
fmt.Println(z)
}
output of the above code is: [1 2 3 4 5 6]

To concatenate two slices,
func main() {
s1 := []int{1, 2, 3}
s2 := []int{99, 100}
s1 = append(s1, s2...)
fmt.Println(s1) // [1 2 3 99 100]
}
To append a single value to a slice
func main() {
s1 := []int{1,2,3}
s1 := append(s1, 4)
fmt.Println(s1) // [1 2 3 4]
}
To append multiple values to a slice
func main() {
s1 := []int{1,2,3}
s1 = append(s1, 4, 5)
fmt.Println(s1) // [1 2 3 4]
}

Seems like a perfect use for generics (if using 1.18 or later).
func concat[T any](first []T, second []T) []T {
n := len(first);
return append(first[:n:n], second...);
}

append([]int{1,2}, []int{3,4}...) will work. Passing arguments to ... parameters.
If f is variadic with a final parameter p of type ...T, then within f the type of p is equivalent to type []T.
If f is invoked with no actual arguments for p, the value passed to p is nil.
Otherwise, the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T. The length and capacity of the slice is therefore the number of arguments bound to p and may differ for each call site.
Given the function and calls
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

Related

Unexpected result when removing an element from a slice

I encountered unexpected results when I remove the first element from slice, this is my test code, hope to help you understand my confuse
define structure
type A struct {
member int
}
func (a A) String() string {
return fmt.Sprintf("%v", a.member)
}
type B struct {
a A
aPoint *A
}
func (b B) String() string {
return fmt.Sprintf("a: %v, aPoint: %v", b.a, b.aPoint)
}
below is my test case
func TestSliceRemoveFirstEle(t *testing.T) {
demo := []B{
{a: A{member: 1}},
{a: A{member: 2}},
{a: A{member: 3}},
}
for i := range demo {
demo[i].aPoint = &demo[i].a
}
fmt.Println("demo before operation is ", demo) // result: demo before operation is [a: 1, aPoint: 1 a: 2, aPoint:2 a: 3, aPoint: 3]
demo = append(demo[:0], demo[1:]...)
fmt.Println("demo after operation is ", demo) // result: demo after operation is [a: 2, aPoint: 3 a: 3, aPoint: 3]
}
my expected result is [a: 2, aPoint: 2 a: 3, aPoint: 3]
when I use another way adding element to the slice, it work well.
func TestAddSliceRemoveFirstEle(t *testing.T) {
demo := make([]B, 0, 3)
a1 := A{member: 1}
a2 := A{member: 2}
a3 := A{member: 3}
demo = append(demo, B{a: a1, aPoint: &a1}, B{a: a2, aPoint: &a2}, B{a: a3, aPoint: &a3})
fmt.Println("demo before operation is ", demo) // result: demo before operation is [a: 1, aPoint: 1 a: 2, aPoint: 2 a: 3, aPoint: 3]
demo = append(demo[:0], demo[1:]...)
fmt.Println("demo after operation is ", demo) // result: demo after operation is [a: 2, aPoint: 2 a: 3, aPoint: 3]
}
I'm confused about the result, what happened in the first case after removing element from slice, and what's the difference between these two implementations?
In your first example all the A structs exist only in the slice and the pointer points to elements of the slice.
In your second example, the A structs exist outside the slice and so there are no pointers against the elements of the slice.
As per the golang append specification at https://golang.org/ref/spec#Appending_and_copying_slices
If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.
Since we are decreasing the size the underlying array is big enough so we are reusing it.
This is what the underlying array of capacity 3 looks like before and after the reslicing
123 - before
233 - after
What pointed at '2' before is now pointing at '3' as your example shows, a simpler way to see can be through this playground
package main
import (
"fmt"
)
func main() {
mySlice := []int{1, 2, 3}
one := &mySlice[0]
two := &mySlice[1]
three := &mySlice[2]
fmt.Printf("%v-%d\n%v-%d\n%v-%d\n",one,*one,two,*two,three,*three)
fmt.Printf("%v cap %d\n",mySlice, cap(mySlice))
mySlice = append(mySlice[:1],mySlice[2:]...)
fmt.Printf("%v cap %d\n",mySlice, cap(mySlice))
fmt.Printf("%v-%d\n%v-%d\n%v-%d\n",one,*one,two,*two,three,*three)
}
Where you'll see
0xc0000be000-1
0xc0000be008-2
0xc0000be010-3
[1 2 3] cap 3
[1 3] cap 3
0xc0000be000-1
0xc0000be008-3
0xc0000be010-3
In short, taking pointers out of elements of a slice and reslicing it will always cause these kind of errors.
It could also cause memory leak issues as per the footnote here https://github.com/golang/go/wiki/SliceTricks#delete-without-preserving-order
Essentially the aPoint pointers still point to the same address, but the values at those addresses changed.
After the append it is demo[0].aPoint == &demo[1].a and not demo[0].aPoint == &demo[0].a.
https://play.golang.org/p/HoNhFlxTEFN
The slice expression demo[:0] will result in a new slice of length 0 that points to the same underlying array as the demo slice. The elements stored in that array are still there, they are not discarded.
So after demo[:0] the underlying array is still:
[
B{a:1,<pointer_to_idx:0_field_a>},
B{a:2,<pointer_to_idx:1_field_a>},
B{a:3,<pointer_to_idx:2_field_a>},
]
demo[1:]... drops the first element and passes the remaining two elements to append. Then append takes those two elements and updates the underlying array's first two elements. After that the underlying array will look like this:
[
B{a:2,<pointer_to_idx:1_field_a>},
B{a:3,<pointer_to_idx:2_field_a>},
B{a:3,<pointer_to_idx:2_field_a>},
]
Notice that now, the aPointer field of the 0th element of the array points to the 1st element's a field. And the aPointer field of the 1st element points to the 2nd element's a field.

Best way to remove selected elements from slice

I have a slice A and another slice B. Slice A contains n elements and slice B is a subset of slice A where each element is a pointer to Slice A.
What would be the cheapest method to remove all elements from A which is referred in B.
After bit of googling only method I can think of is to reslice slice A for each element in B. Is that is only method or is there a simpler one?
I have a slice A and another slice B. Slice A contains n elements and
slice B is a subset of slice A where each element is a pointer to
Slice A.
What would be the cheapest method to remove all elements from A which
is referred in B.
A and B may have duplicates and may not be sorted.
For example, growth rate O(n),
package main
import "fmt"
func remove(a []int, b []*int) []int {
d := make(map[*int]bool, len(b))
for _, e := range b {
d[e] = true
}
var c []int
if len(a) >= len(d) {
c = make([]int, 0, len(a)-len(d))
}
for i := range a {
if !d[&a[i]] {
c = append(c, a[i])
}
}
return c
}
func main() {
a := []int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println(a)
b := []*int{&a[1], &a[3], &a[3], &a[7], &a[4]}
a = remove(a, b)
fmt.Println(a)
}
Playground: https://play.golang.org/p/-RpkH51FSt2
Output:
[0 1 2 3 4 5 6 7]
[0 2 5 6]

Golang slice append built-in function returning value

The return value of slice2 of this code is [[1 1][1 1]].
And that got me confuse because I was expecting [[0 0][1 1]].
I can't figure it out why is returning [[1 1][1 1]] and not [[0 0][1 1]].
I would appreciate if someone can explain that. Thanks.
slice := []int{0, 0}
slice2 := [][]int{}
for i := range slice {
slice[0] = i
slice[1] = i
slice2 = append(slice2, slice)
}
fmt.Println(slice2)
You can check the code in this link
play.golang.org
A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).
In your case your are appending a slice(pointer) not the slice's values. To get the desired result you have to declare a new slice in each iteration and append the new slice to slice2.
func main() {
slice := []int{0, 0}
slice2 := [][]int{}
for i := range slice {
ns := []int{0, 0}
ns[0] = i
ns[1] = i
slice2 = append(slice2, ns)
}
fmt.Println(slice2) // Outputs: [[0 0] [1 1]]
}
Playground
When you range over the slice, it is not giving you the VALUES of the slice, it is giving you the indexes. This for loop is equivalent to for i:=0; i < len(slice); i++ {}. So by the time it gets through iteration, both values are in slice are 1, and you have appended it to slice2 twice.
If you wanted the values of the slice, you would need to do for i, v := range slice {} which would give you both the indexes and the values. You could use _ in place of i if you didn't want the index.
Also note that appending slice twice like this appends the same exact slice because slices are allocated as pointers on the heap and it is therefore a pointer to the slice. Thus slice2[0] == slice2[1] because it is the same exact slice.
The problem is that what you append to slice2 is a reference to slice, not the values contained in slice at the time of the call.
This means that at the end, slice2 contains two pointers to the same slice. Any changes to the slice variable will be reflected to slice2 and vice-versa: they point to the same location in memory.
As a proof, just do:
slice2[0][1] = 2
fmt.Println(slice2)
fmt.Println(slice)
You will get:
[[1 2] [1 2]]
[1 2]

How to append a new element to a slice?

I tried to append a new element to a slice, just like appending a new element to a list in Python, but there is an error
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
s[len(s)] = 100
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("%v\n", s)
}
and the output is
[2 3 5 7 11 13]
panic: runtime error: index out of range
How can I append a new element to a slice then?
Is it correct that slice in Go is the closet data type to list in Python?
Thanks.
Appending in Go uses the builtin method append. For example:
s := []byte{1, 2, 3}
s = append(s, 4, 5, 6)
fmt.Println(s)
// Output: [1 2 3 4 5 6]
You are correct, slices in Go are close to Python's list, and, in fact, many of the same indexing and slicing operations work on them.
When you do:
s[len(s)] = 100
it panics because you cannot access a value beyond the length of the slice. There appears to be some confusion here, because in Python this works the exact same way. Attempting the above in Python (unless you've already expanded the list) will give:
IndexError: list assignment index out of range

Golang: appending slices with or w/o allocation

Go's append() function only allocates new slice data, when the capacity of the given slice is not sufficient (see also: https://stackoverflow.com/a/28143457/802833). This can lead to unexpected behavior (at least for me as a golang newbie):
package main
import (
"fmt"
)
func main() {
a1 := make([][]int, 3)
a2 := make([][]int, 3)
b := [][]int{{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}
common1 := make([]int, 0)
common2 := make([]int, 0, 12) // provide sufficient capacity
common1 = append(common1, []int{10, 20}...)
common2 = append(common2, []int{10, 20}...)
idx := 0
for _, k := range b {
a1[idx] = append(common1, k...) // new slice is allocated
a2[idx] = append(common2, k...) // no allocation
idx++
}
fmt.Println(a1)
fmt.Println(a2) // surprise!!!
}
output:
[[10 20 1 1 1] [10 20 2 2 2] [10 20 3 3 3]]
[[10 20 3 3 3] [10 20 3 3 3] [10 20 3 3 3]]
https://play.golang.org/p/8PEqFxAsMt
So, what ist the (idomatic) way in Go to force allocation of new slice data or more precisely to make sure that the slice argument to append() remains unchanged?
You might maintain a wrong idea of how slices work in Go.
When you append elements to a slice, the call to append() returns a new slice. If reallocation did not happen, both slice values — the one you called append() on and the one it returned back — share the same backing array but they will have different lengths; observe:
package main
import "fmt"
func main() {
a := make([]int, 0, 10)
b := append(a, 1, 2, 3)
c := append(a, 4, 3, 2)
fmt.Printf("a=%#v\nb=%#v\nc=%#v\n", a, b, c)
}
outputs:
a=[]int{}
b=[]int{4, 3, 2}
c=[]int{4, 3, 2}
So, len(a) == 0, len(b) == 3, len(c) == 3, and the second call to append() owerwrote what the first one did because all the slices share the same underlying array.
As to reallocation of the backing array, the spec is clear:
If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.
From this, it follows that:
append() never copies the underlying storage if the capacity of the slice being appeneded to is sufficient.
If there's not enough capacity, the array will be reallocated.
That is, given a slice s to which you want to append N elements, the reallocation won't be done iff cap(s) - len(s) ≥ N.
Hence I suspect your problem is not about unexpected reallocation results but rather about the concept of slices as implemented in Go. The code idea to absorb is that append() returns the resulting slice value, which you're supposed to be using after the call unless you fully understand the repercussions.
I recommend starting with this to fully understand them.
Thanx for your feedback.
So the solution to gain control of the memory allocation is to do it explicitely (which remembers me that Go is a more a system language than other (scripting) langs):
package main
import (
"fmt"
)
func main() {
a1 := make([][]int, 3)
a2 := make([][]int, 3)
b := [][]int{{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}
common1 := make([]int, 0)
common2 := make([]int, 0, 12) // provide sufficient capacity
common1 = append(common1, []int{10, 20}...)
common2 = append(common2, []int{10, 20}...)
idx := 0
for _, k := range b {
a1[idx] = append(common1, k...) // new slice is allocated
a2[idx] = make([]int, len(common2), len(common2)+len(k))
copy(a2[idx], common2) // copy & append could probably be
a2[idx] = append(a2[idx], k...) // combined into a single copy step
idx++
}
fmt.Println(a1)
fmt.Println(a2)
}
output:
[[10 20 1 1 1] [10 20 2 2 2] [10 20 3 3 3]]
[[10 20 1 1 1] [10 20 2 2 2] [10 20 3 3 3]]
https://play.golang.org/p/Id_wSZwb84

Resources