The most concise way to concatenate 3 or more slices - go

I am looking for a way to concisely and efficiently concatenate 3 or more slices in Go.
Let's say I want to concatenate the following slices (all the code can be found here - https://play.golang.org/p/6682YiFF8qG):
a := []int{1, 2, 3}
b := []int{4, 5, 6}
c := []int{7, 8, 9}
My first attempt is by using the append method:
d1 := append(a, b...)
d1 = append(d1, c...) // [1 2 3 4 5 6 7 8 9]
However, this method is verbose and requires 2 append calls for concatenating three slices. So, for n slices, I will need n-1 calls to append, which is not only verbose, but also inefficient as it requires multiple allocations.
My next attempt is to create a variadic function to handle the concatenation with only one new slice allocation:
func concat(slicesOfSlices ...[]int) []int {
var totalLengthOfSlices int
for _, slice := range slicesOfSlices {
totalLengthOfSlices += len(slice)
}
arr := make([]int, 0, totalLengthOfSlices)
for _, slice := range slicesOfSlices {
arr = append(arr, slice...)
}
return arr
}
Then I can use it as follows:
d2 := concat(a, b, c) // [1 2 3 4 5 6 7 8 9]
To illustrate, I want to emulate the following convenient functionality of the spread operator in JavaScript, which I often use in the following way:
const a = [1, 2, 3];
const b = [4, 5, 6];
const c = [7, 8, 9];
const d = [...a, ...b, ...c]; // [1, 2, 3, 4, 5, 6, 7, 8, 9]
In other words, I am looking for a way to do something like d3 := append(a, b, c) or d3 := append(a, b..., c...) but with the standard Go library or using less code than I did.
Note on possible duplicates
I don't think this is a duplicate of the question "How to concatenate two slices" as my question is about concatenating 3 or more slices in the most concise and idiomatic way.

You could use your first method of using append like this:
a := []int{1, 2, 3, 4}
b := []int{9, 8, 7, 6}
c := []int{5, 4, 3, 2}
a = append(a, append(b, c...)...)
That being said, I think that your variadic concat function is cleaner and isn't very much code for a utility function.
(Go Playground Link)
Good luck!

Related

Why appending on slice modified another slice? [duplicate]

This question already has answers here:
Why does append() modify the provided slice? (See example)
(1 answer)
Golang append changing append parameter [duplicate]
(2 answers)
Why does append modify passed slice
(5 answers)
Deleting element of slice modifies original value [duplicate]
(1 answer)
Closed last year.
package main
import "fmt"
func main() {
src := []int{0, 1, 2, 3, 4, 5, 6}
a := src[:3]
b := src[3:]
a = append(a, 9)
fmt.Println(a, b)
}
output:
[0 1 2 9] [9 4 5 6]
Did append modified the underlay array as []int{0, 1, 2, 9, 4, 5, 6}?
Slice a was copied as a new slice with a new underlay array with value [0, 1, 2, 9] and slice b still pointing to the old array that was modified.
Thanks for any hints, much appreciated
Slice a was copied as a new slice with a new underlay array with value [0, 1, 2, 9] and slice b still pointing to the old array that was modified.
a := src[:3] created a slice (a pointer to the src head, length=3, capacity=7)
b := src[3:] created a slice(a pointer to the src[3],length=4, capacity=4)
a and b shares the same memory created by src
a = append(a, 9),when appending to the same slice, as long as it does not exceed cap, it is the same array that is modified
Did append modified the underlay array as []int{0, 1, 2, 9, 4, 5, 6}
YES
If the append exceed the cap of a, new array will be allocated and data will be copied the the new array
try this out:
package main
import "fmt"
func main() {
src := []int{0, 1, 2, 3, 4, 5, 6}
a := src[:3]
b := src[3:]
a = append(a, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9)
fmt.Println(a, b)
}
Output:
[0 1 2 9 9 9 9 9 9 9 9 9 9] [3 4 5 6]

joining 2 dimensional array by columns

I have 2 dimensional array on golang something like
array which contains [1,2,3,4]
[2,3,4,5]
[3,4,5,6]
and I want to join the array columns i.e the result should be
1,2,3
2,3,4
3,4,5
4,5,6
my approach is something like this to create 4 arrays and do something like this:
a := []int{}
for _, row := range array {
append (a,array[1])
append (b,array[2])
append (c,array[2])
append (d,array[2])
}
and then join those arrays something like this
fmt.Println(strings.Join(a[:], ","))
fmt.Println(strings.Join(b[:], ","))
fmt.Println(strings.Join(c[:], ","))
fmt.Println(strings.Join(d[:], ","))
my question if there are an option to access the array by columns not by row or if there are more usful way to do this?
Instead of instantiating multiple arrays, you can just work with another two-dimensional array.
Here's a quick pass at an implementation (it could use more error checking, e.g. assumes all of the inner arrays in the input are the same length):
package main
import (
"fmt"
)
func transpose(input [][]int) [][]int {
result := make([][]int, len(input[0]))
for _, row := range(input) {
for j, value := range(row) {
result[j] = append(result[j], value)
}
}
return result
}
func main() {
input := [][]int{{1,2,3,4}, {2,3,4,5}, {3,4,5,6}}
result := transpose(input)
fmt.Println(input)
fmt.Println(result)
}
Output:
[[1 2 3 4] [2 3 4 5] [3 4 5 6]]
[[1 2 3] [2 3 4] [3 4 5] [4 5 6]]
Go Playground
flatten the list of list to a single list. slice the list into intervals using itemgetter and assemble into a single list.
from operator import itemgetter
data=[[1,2,3,4],[2,3,4,5], [3,4,5,6]]
data = [item for sublist in data for item in sublist]
index=[slice(0,4),slice(4,8),slice(8,13)]
result=[]
for idx_slice in index:
f = itemgetter(idx_slice)
col1=f(data)
result.append(col1)
print(result)
output:
[[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]

In Go, how do I duplicate the last element of a slice?

If I have a Python list, it is simple to duplicate the last element: l.append(l[-1]). Is there an elegant way to do this with Go slices?
The best I have so far is data = append(data, data[len(data) - 1]).
That is the proper way to do it.
In Go you can't use negative indices, so the index of the last element is len(data) -1.
And in Go append() is a builtin function and not a method of slices, and it returns a new slice value which you have to assign or store if you need the extended slice, so there's nothing you can make shorter in your code in general.
Note that if you were on to create your own type, you could attach methods to it which could simpify things. For example:
type ints []int
func (i *ints) append(es ...int) {
*i = append(*i, es...)
}
func (i *ints) appendLast() {
*i = append(*i, (*i)[len(*i)-1])
}
Using it:
is := ints{1, 2, 3}
fmt.Println(is)
is.append(4, 5) // Append some elements
fmt.Println(is)
is.append(is[len(is)-1]) // Append last element explicitly
fmt.Println(is)
is.appendLast() // Append last element using appendLast method
fmt.Println(is)
// You can also use it for []int values, with conversion:
is2 := []int{1, 2, 3}
(*ints)(&is2).appendLast()
fmt.Println(is2)
Output (try it on the Go Playground):
[1 2 3]
[1 2 3 4 5]
[1 2 3 4 5 5]
[1 2 3 4 5 5 5]
[1 2 3 3]
You can also use simple util functions without creating a new type:
func appendLast(i []int) []int {
return append(i, i[len(i)-1])
}
func appendLast2(i *[]int) {
*i = append(*i, (*i)[len(*i)-1])
}
func main() {
is := []int{1, 2, 3}
fmt.Println(is)
is = appendLast(is)
fmt.Println(is)
appendLast2(&is)
fmt.Println(is)
}
Output (try it on the Go Playground):
[1 2 3]
[1 2 3 3]
[1 2 3 3 3]
data = append(data, data[len(data) - 1])
Yes. It's the best way to do in Go. It's not possible to use a negative index in Go.

Concatenate 2 slices in golang

I have 2 slices,
s1 := []int{1, 2, 3, 4, 5}
s2 := []int{3, 4, 5, 6, 7}
I want the resultant
s3 = []int{1, 2, 3, 4, 5, 3, 4, 5, 6, 7}
I am doing something like:
for _, x := range s1 {
s2 = append(s2, x)
}
This looks very trivial question but trust me I don't find a one line solution to this.
How can we go about this?
This is what the builtin append() function is for: to append values (which may be values of a slice) to the end of another. And the result is the concatenation.
If you want s3 to be "independent" of s1 and s2, then append s1 to an empty or nil slice, then append s2 to the result:
s3 := append(append([]int{}, s1...), s2...)
fmt.Println(s3)
If s3 may use / overlap s1, you can simply append s2 to s1:
s4 := append(s1, s2...)
fmt.Println(s4)
Output in both cases (try it on the Go Playground):
[1 2 3 4 5 3 4 5 6 7]
Note: What this "overlapping" means is that if you append values to s1, if it has enough capacity, no new slice will be allocated, s1 will be resliced to have enough length to accommodate the elements you want to append to it. This may have surprising side-effects if used unwisely, like in this example:
arr := [...]int{1, 2, 3, 4, 5, 6, 7, 0, 0, 0}
s1 := arr[:5]
s2 := arr[2:7]
fmt.Println("s1:", s1, "cap:", cap(s1))
fmt.Println("s2:", s2)
s3 := append(s1, s2...)
fmt.Println("s3:", s3)
fmt.Println("s2:", s2)
Output is (try it on the Go Playground):
s1: [1 2 3 4 5] cap: 10
s2: [3 4 5 6 7]
s3: [1 2 3 4 5 3 4 5 6 7]
s2: [3 4 5 3 4]
What may be surprising here is that when we appended s2 to s1 and stored the result in s3 (which is what we expected), contents (elements) of s2 also changed. The reason for this is that append() saw that s1 has enough capacity to append s2 to it (elements of s2), so it did not create a new array, it just resliced s1 and added elements "in-place". But the area where the additional elements were written is the exact same memory where elements of s2 reside, so elements of s2 also got overwritten.
I would recommend you to give a look at this page: https://github.com/golang/go/wiki/SliceTricks
It's an official documentation by golang for slices. For your problem you just need to use append :)

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