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

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.

Related

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]]

Why does the keyword range in golang work for 2d-slices?

I'd like to make a copy of a slice of bytes, convert the type from [][4]byte to [][]byte. And I expect the followering code to return [[1 2 3 4] [2 3 4 5]], but actually got [[2 3 4 5] [2 3 4 5]].
package main
import "fmt"
func main() {
ids := [][4]byte{[4]byte{1, 2, 3, 4}, [4]byte{2, 3, 4, 5}}
var slices [][]byte
for _, id := range ids {
slices = append(slices, id[:])
}
fmt.Println(slices)
}
Why?
Why appending id[:] to the slice would overwrite the first element in the slice?
As said by Marc, you are appending a reference via id.
Therefore when you iterate, and id changes its value, your "slices" to which you are appending are getting re-evaluated with id new values.
To avoid this:
for i := range ids {
slices = append(slices, ids[i][:]) // here you are referring to the value at index i of ids
}

append to a new slice affect original slice

I have the following code which causes a weird result. I cannot understand why:
func main() {
var s = []int{2, 3}
var s1 = append(s, 4)
var a = append(s1, 5)
var b = append(s1, 6)
fmt.Println(s)
fmt.Println(s1)
fmt.Println(a)
fmt.Println(b)
}
This then results in:
[2 3]
[2 3 4]
[2 3 4 6]
[2 3 4 6]
My question is: why a is not [2 3 4 5] but [2 3 4 6]? I know append to b changes a, but how. Is this a bug because I never changed a directly and obviously I don't want this happen?
Keep in mind that a slice is a structure of 3 fields.
a pointer to the underlying array
length of the slice
capacity of the slice
append() function may either modify its argument in-place or return a copy of its argument with an additional entry, depending on the size and capacity of its input. append() function creates a new slice, if the length the slice is greater than the length of the array pointed by the slice.

Decreasing slice capacity

My question is about slice length and capacity. I'm learning about Go here: https://tour.golang.org/moretypes/11.
(My question was marked as a possible duplicate of this; however, this is not the case. My question is specifically about the cutting off the first few elements of a slice and the implications of that.)
Why does the line s = s[2:] decrease the capacity when s = s[:4] and s = s[:0] do not? The only difference I see is that there is a number before the colon in s = s[2:] while there is a number after the colon in the other two lines.
Is there any way to recover the first two elements that we cut off with s = s[2:]?
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
// Slice the slice to give it zero length.
s = s[:0]
printSlice(s)
// Extend its length.
s = s[:4]
printSlice(s)
// Drop its first two values.
s = s[2:]
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
After clicking the Run button, we get the following.
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
You can read more about slices here. But I think this passage answers your question:
Slicing does not copy the slice's data. It creates a new slice value that points to the original array. This makes slice operations as efficient as manipulating array indices. Therefore, modifying the elements (not the slice itself) of a re-slice modifies the elements of the original slice.
So you cannot recover the slice data if you are assigning it to the same variable.
The capacity decrease is because by dropping the first 2 elements you are changing the pointer to the new slice (slices are referenced by the pointer to the first element).
How slices are represented in the memory:
make([]byte, 5)
s = s[2:4]
You can use a full slice expression:
package main
func main() {
s := []int{2, 3, 5, 7, 11, 13}
{ // example 1
t := s[:0]
println(cap(t) == 6)
}
{ // example 2
t := s[:0:0]
println(cap(t) == 0)
}
}
https://golang.org/ref/spec#Slice_expressions
The slices.Clip of "golang.org/x/exp/slices" could reduce the capacity of slice through Full slice expressions.
Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
s = s[:4]
printSlice(s)
s = slices.Clip(s)
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
len=6 cap=6 [2 3 5 7 11 13]
len=4 cap=6 [2 3 5 7]
len=4 cap=4 [2 3 5 7]
Playground

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

Resources