How to reuse slice with sync.Pool in golang? - go

Consider this code:
type TestStruct struct {
Name string
}
func TestSliceWithPool(t *testing.T) {
var slicePool = sync.Pool{
New: func() interface{} {
t.Log("i am created")
s := make([]interface{}, 0)
return s
},
}
s, _ := slicePool.Get().([]interface{})
t.Logf("Lenth: %d, Cap: %d, Pointer: %p", len(s), cap(s), s)
for i := 0; i < 9000; i++ {
st := &TestStruct{Name: "test"}
s = append(s, st)
}
for _, v := range s {
if value, ok := v.(TestStruct); ok {
if value.Name != "test" {
t.Error("u are changed!")
}
}
}
s = s[:0]
slicePool.Put(s)
s2, _ := slicePool.Get().([]interface{})
t.Logf("Lenth: %d, Cap: %d, Pointer: %p", len(s), cap(s), s)
for i := 0; i < 8000; i++ {
st := &TestStruct{Name: "test2"}
s2 = append(s2, st)
}
for _, v := range s2 {
if value, ok := v.(TestStruct); ok {
if value.Name != "test2" {
t.Error("u are changed!")
}
}
}
slicePool.Put(s2)
}
The result of test is:
slice_test.go:63: i am created
slice_test.go:70: Lenth: 0, Cap: 0, Pointer: 0x1019598
slice_test.go:86: Lenth: 0, Cap: 9728, Pointer: 0xc000500000
Why is it generated only once but the address is different? And Why the cap is 9728?
Is there any problem when I use the same slice like this?

Why is it generated only once but the address is different?
Because in the first for loop you append to it beyond its capacity, which in New is set to zero, and reassign to it the result of append. For details: Why does append() modify the provided slice? (See example)
Is there any problem when I use the same slice like this?
There could be. When you reslice s with s = s[:0], you are resetting the length but not the capacity. The backing array is still the same one from the previous append-and-reassign operation.
So if you append again to s2, the capacity will be enough to not cause reallocation, and you'll end up overwriting the first elements of the backing array:
A demonstrative example:
func TestSliceWithPool(t *testing.T) {
var slicePool = sync.Pool{
New: func() interface{} {
t.Log("Created")
s := make([]interface{}, 0)
return s
},
}
s, _ := slicePool.Get().([]interface{})
for i := 0; i < 10; i++ {
s = append(s, i)
}
fmt.Println(s)
// ^ output: [0 1 2 3 4 5 6 7 8 9]
s = s[:0]
slicePool.Put(s)
s2, _ := slicePool.Get().([]interface{})
fmt.Println(s)
// ^ output: []
for i := 0; i < 5; i++ {
s2 = append(s2, i*10)
}
fmt.Println(s2)
// ^ output: [0 10 20 30 40]
fmt.Println(s2[:10])
// ^ output: [0 10 20 30 40 5 6 7 8 9]
}
This might be okay, since you now have a slice with extended capacity that doesn't need reallocation on append, but it could also be a memory leak if your application keeps around other slice headers pointing to the same backing array (as in case of s and s2), thus preventing garbage collection of the buffers.

Related

Why slice kept escaping from stack?

I am trying to solve leetcode problem permutations.
But when i test with -benchmem, i found it allocs too much which reach 1957 allocs/op when permute([]int{1,2,3,4,5,6})
I found it escape to heap when generating sub-nums target. Even i try to allocate [6]int, and use unsafe package to build the slice, it still moved to heap.
My question is, why the slice escape to heap, and how could i allocate the slice on stack?
Here's my code:
package main
import (
"fmt"
"reflect"
"unsafe"
)
func permute(nums []int) [][]int {
resLen := 1
for i := 1; i<= len(nums);i ++{
resLen *= i
}
// pre allocate
res := make([][]int, resLen)
for i := range res{
res[i] = make([]int, 0, len(nums))
}
build(res, nums)
return res
}
func build(res [][]int,targets []int){
step := len(res) / len(targets)
for i := range targets{
for j := i*step; j < (i+1) * step; j ++{
res[j] = append(res[j], targets[i])
}
if len(targets) != 1{
var ab = [6]int{}
var buff []int
var bp *reflect.SliceHeader
bp = (*reflect.SliceHeader)(unsafe.Pointer(&buff))
bp.Data = uintptr(unsafe.Pointer(&ab))
bp.Cap = 6
buff = append(buff, targets[:i]...)
buff = append(buff, targets[i+1:]...)
build(res[i*step:(i+1)*step], buff)
}
}
return
}
func main() {
nums := []int{1,2,3}
res := permute(nums)
fmt.Println(res)
}
build function without unsafe but escapes to heap:
func build(res [][]int, targets []int) {
step := len(res) / len(targets)
for i := range targets {
for j := i * step; j < (i+1)*step; j++ {
res[j] = append(res[j], targets[i])
}
if len(targets) != 1 {
buff := make([]int, 0, 6) // make([]int, 0, 6) escapes to heap
buff = append(buff, targets[:i]...)
buff = append(buff, targets[i+1:]...)
build(res[i*step:(i+1)*step], buff)
}
}
return
}
And my test case:
package main
import "testing"
func Benchmark(b *testing.B){
for i:=0;i<b.N;i++{
permute([]int{1,2,3,4,5,6})
}
}
When i run go build -gcflags="-m", it reports ./main.go:32:8: moved to heap: ab
Trying to subvert the compiler using unsafe.Pointer is only making it harder for the escape analysis to do its job, preventing the slice from being stack allocated. Simply allocate a single slice and reuse it for each loop iteration:
func build(res [][]int, targets []int) {
buff := make([]int, 0, 6)
step := len(res) / len(targets)
for i := range targets {
buff = buff[:0]
for j := i * step; j < (i+1)*step; j++ {
res[j] = append(res[j], targets[i])
}
if len(targets) != 1 {
buff = append(buff, targets[:i]...)
buff = append(buff, targets[i+1:]...)
build(res[i*step:(i+1)*step], buff)
}
}
return
}
This can be correctly optimized by the compiler
./main.go:26:17: make([]int, 0, 6) does not escape
And will result in only the desired allocations:
Benchmark-8 44607 26838 ns/op 52992 B/op 721 allocs/op

How to get this specific shape for my data

In Go, I'm using this function bars, err := custplotter.NewCandlesticks(data)
from here:
https://github.com/pplcc/plotext/tree/master/custplotter
It's expecting this shape for data:
[{2 16435 16458 16435 16446 1} {3 16446 16458 16435.04 16455 1} .....]
But my code below is creating my data in this shape instead:
[[2 16435 16458 16435 16446 1] [3 16446 16458 16435.04 16455 1] .....]
Which gives me this error message:
cannot use data (type [ ][ ]string) as type custplotter.TOHLCVer in argument to custplotter.NewCandlesticks:
[ ][ ]string does not implement custplotter.TOHLCVer (missing Len method)
I believe the problem is the data shape. How can i change my code to create the required data shape (with { } instead of [ ]) ?
//read excel file******************************************
xlsx, err := excelize.OpenFile("/media/Snaps/test snaps.xlsm")
if err != nil {
fmt.Println(err)
return
}
//read all rows into df
df := xlsx.GetRows("ticker_2")
//get only TOHLCV columns and 60 rows
df3 := make([][]string, 60) // create slice for 60 rows
idx := 0
for _, row := range df[1:61] { // read 60 rows
df3row := make([]string, 6) // create slice for 6 columns
copy(df3row, row[28:34]) // copy desired columns to new row slice
df3[idx] = df3row
idx++
}
All examples of slices i found in Go litterature uses only [ [ ], [ ] ]
as per https://github.com/pplcc/plotext/blob/68ab3c6e05c34baf5af21c9f5c3341f527a110ac/examples/tohlcvexampledata.go#L42
It seems that what you need is a custplotter.TOHLCVs which is just a slice of a struct of float64.
https://github.com/pplcc/plotext/blob/master/custplotter/tohlcv.go:
type TOHLCVer interface {
// Len returns the number of time, open, high, low, close, volume tuples.
Len() int
// TOHLCV returns an time, open, high, low, close, volume tuple.
TOHLCV(int) (float64, float64, float64, float64, float64, float64)
}
// TOHLCVs implements the TOHLCVer interface using a slice.
type TOHLCVs []struct{ T, O, H, L, C, V float64 }
so basically your solution could resemble this:
df3 := make(TOHLCVs, 60) // create slice for 60 rows
idx := 0
for _, row := range df[1:61] { // read 60 rows
df3[idx].T, err = strconv.ParseFloat(row[28], 64)
df3[idx].O, err = strconv.ParseFloat(row[29], 64)
df3[idx].H, err = strconv.ParseFloat(row[30], 64)
df3[idx].L, err = strconv.ParseFloat(row[31], 64)
df3[idx].C, err = strconv.ParseFloat(row[32], 64)
df3[idx].V, err = strconv.ParseFloat(row[33], 64)
idx++
}
Or you could just implement the TOHLCVer interface too :)
type SlicesOfTOHLCV [][6]float64
func (s SlicesOfTOHLCV) Len() int {
return len(s)
}
func (s SlicesOfTOHLCV) TOHLCV(i int) (float64, float64, float64, float64, float64) {
return s[i][0], s[i][1], s[i][2], s[i][3], s[i][4], s[i][5]
}
mySlice := make(SlicesOfTOHLCV, 60)
i := 0
for _, row := range df[1:61] {
mySlice[i] = [6]float64{}
for j := 0; j < 6; j ++ {
mySlice[i][j], err = strconv.ParseFloat(row[28+j], 64)
if err != nil {
panic(err)
}
}
i ++
}

check for equality on slices without order

I am trying to find a solution to check for equality in 2 slices. Unfortanely, the answers I have found require values in the slice to be in the same order. For example, http://play.golang.org/p/yV0q1_u3xR evaluates equality to false.
I want a solution that lets []string{"a","b","c"} == []string{"b","a","c"} evaluate to true.
MORE EXAMPLES
[]string{"a","a","c"} == []string{"c","a","c"} >>> false
[]string{"z","z","x"} == []string{"x","z","z"} >>> true
Here is an alternate solution, though perhaps a bit verbose:
func sameStringSlice(x, y []string) bool {
if len(x) != len(y) {
return false
}
// create a map of string -> int
diff := make(map[string]int, len(x))
for _, _x := range x {
// 0 value for int is 0, so just increment a counter for the string
diff[_x]++
}
for _, _y := range y {
// If the string _y is not in diff bail out early
if _, ok := diff[_y]; !ok {
return false
}
diff[_y] -= 1
if diff[_y] == 0 {
delete(diff, _y)
}
}
return len(diff) == 0
}
Try it on the Go Playground
You can use cmp.Diff together with cmpopts.SortSlices:
less := func(a, b string) bool { return a < b }
equalIgnoreOrder := cmp.Diff(x, y, cmpopts.SortSlices(less)) == ""
Here is a full example that runs on the Go Playground:
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
func main() {
x := []string{"a", "b", "c"}
y := []string{"a", "c", "b"}
less := func(a, b string) bool { return a < b }
equalIgnoreOrder := cmp.Diff(x, y, cmpopts.SortSlices(less)) == ""
fmt.Println(equalIgnoreOrder) // prints "true"
}
The other answers have better time complexity O(N) vs (O(N log(N)), that are in my answer, also my solution will take up more memory if elements in the slices are repeated frequently, but I wanted to add it because I think this is the most straight forward way to do it:
package main
import (
"fmt"
"sort"
"reflect"
)
func array_sorted_equal(a, b []string) bool {
if len(a) != len(b) {return false }
a_copy := make([]string, len(a))
b_copy := make([]string, len(b))
copy(a_copy, a)
copy(b_copy, b)
sort.Strings(a_copy)
sort.Strings(b_copy)
return reflect.DeepEqual(a_copy, b_copy)
}
func main() {
a := []string {"a", "a", "c"}
b := []string {"c", "a", "c"}
c := []string {"z","z","x"}
d := []string {"x","z","z"}
fmt.Println( array_sorted_equal(a, b))
fmt.Println( array_sorted_equal(c, d))
}
Result:
false
true
I would think the easiest way would be to map the elements in each array/slice to their number of occurrences, then compare the maps:
func main() {
x := []string{"a","b","c"}
y := []string{"c","b","a"}
xMap := make(map[string]int)
yMap := make(map[string]int)
for _, xElem := range x {
xMap[xElem]++
}
for _, yElem := range y {
yMap[yElem]++
}
for xMapKey, xMapVal := range xMap {
if yMap[xMapKey] != xMapVal {
return false
}
}
return true
}
You'll need to add some additional due dilligence, like short circuiting if your arrays/slices contain elements of different types or are of different length.
Generalizing the code of testify ElementsMatch, solution to compare any kind of objects (in the example []map[string]string):
https://play.golang.org/p/xUS2ngrUWUl
Like adrianlzt wrote in his answer, an implementation of assert.ElementsMatch from testify can be used to achieve that. But how about reusing actual testify module instead of copying that code when all you need is a bool result of the comparison? The implementation in testify is intended for tests code and usually takes testing.T argument.
It turns out that ElementsMatch can be quite easily used outside of testing code. All it takes is a dummy implementation of an interface with ErrorF method:
type dummyt struct{}
func (t dummyt) Errorf(string, ...interface{}) {}
func elementsMatch(listA, listB interface{}) bool {
return assert.ElementsMatch(dummyt{}, listA, listB)
}
Or test it on The Go Playground, which I've adapted from the adrianlzt's example.
Since I haven't got enough reputation to comment, I have to post yet another answer with a bit better code readability:
func AssertSameStringSlice(x, y []string) bool {
if len(x) != len(y) {
return false
}
itemAppearsTimes := make(map[string]int, len(x))
for _, i := range x {
itemAppearsTimes[i]++
}
for _, i := range y {
if _, ok := itemAppearsTimes[i]; !ok {
return false
}
itemAppearsTimes[i]--
if itemAppearsTimes[i] == 0 {
delete(itemAppearsTimes, i)
}
}
if len(itemAppearsTimes) == 0 {
return true
}
return false
}
The logic is the same as in this answer
I know its been answered but still I would like to add my answer. By following code here stretchr/testify we can have something like
func Elementsmatch(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
if aLen != bLen {
return fmt.Sprintf("Len of the lists don't match , len listA %v, len listB %v", aLen, bLen), false
}
visited := make([]bool, bLen)
for i := 0; i < aLen; i++ {
found := false
element := listA[i]
for j := 0; j < bLen; j++ {
if visited[j] {
continue
}
if element == listB[j] {
visited[j] = true
found = true
break
}
}
if !found {
return fmt.Sprintf("element %s appears more times in %s than in %s", element, listA, listB), false
}
}
return "", true
}
Now lets talk about performance of this solution compared to map based ones. Well it really depends on the size of the lists which you are comparing, If size of list is large (I would say greater than 20) then map approach is better else this would be sufficent.
Well on Go PlayGround it shows 0s always, but run this on local system and you can see the difference in time taken as size of list increases
So the solution I propose is, adding map based comparision from above solution
func Elementsmatch(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
if aLen != bLen {
return fmt.Sprintf("Len of the lists don't match , len listA %v, len listB %v", aLen, bLen), false
}
if aLen > 20 {
return elementsMatchByMap(listA, listB)
}else{
return elementsMatchByLoop(listA, listB)
}
}
func elementsMatchByLoop(listA, listB []string) (string, bool) {
aLen := len(listA)
bLen := len(listB)
visited := make([]bool, bLen)
for i := 0; i < aLen; i++ {
found := false
element := listA[i]
for j := 0; j < bLen; j++ {
if visited[j] {
continue
}
if element == listB[j] {
visited[j] = true
found = true
break
}
}
if !found {
return fmt.Sprintf("element %s appears more times in %s than in %s", element, listA, listB), false
}
}
return "", true
}
func elementsMatchByMap(x, y []string) (string, bool) {
// create a map of string -> int
diff := make(map[string]int, len(x))
for _, _x := range x {
// 0 value for int is 0, so just increment a counter for the string
diff[_x]++
}
for _, _y := range y {
// If the string _y is not in diff bail out early
if _, ok := diff[_y]; !ok {
return fmt.Sprintf(" %v is not present in list b", _y), false
}
diff[_y] -= 1
if diff[_y] == 0 {
delete(diff, _y)
}
}
if len(diff) == 0 {
return "", true
}
return "", false
}

How to find the difference between two slices of strings

Here is my desired outcome
slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar"}
difference(slice1, slice2)
=> ["hello"]
I am looking for the difference between the two string slices!
Assuming Go maps are ~O(1), here is an ~O(n) difference function that works on unsorted slices.
// difference returns the elements in `a` that aren't in `b`.
func difference(a, b []string) []string {
mb := make(map[string]struct{}, len(b))
for _, x := range b {
mb[x] = struct{}{}
}
var diff []string
for _, x := range a {
if _, found := mb[x]; !found {
diff = append(diff, x)
}
}
return diff
}
Depending on the size of the slices, different solutions might be best.
My answer assumes order doesn't matter.
Using simple loops, only to be used with smaller slices:
package main
import "fmt"
func difference(slice1 []string, slice2 []string) []string {
var diff []string
// Loop two times, first to find slice1 strings not in slice2,
// second loop to find slice2 strings not in slice1
for i := 0; i < 2; i++ {
for _, s1 := range slice1 {
found := false
for _, s2 := range slice2 {
if s1 == s2 {
found = true
break
}
}
// String not found. We add it to return slice
if !found {
diff = append(diff, s1)
}
}
// Swap the slices, only if it was the first loop
if i == 0 {
slice1, slice2 = slice2, slice1
}
}
return diff
}
func main() {
slice1 := []string{"foo", "bar", "hello"}
slice2 := []string{"foo", "world", "bar", "foo"}
fmt.Printf("%+v\n", difference(slice1, slice2))
}
Output:
[hello world]
Playground: http://play.golang.org/p/KHTmJcR4rg
I use the map to solve this problem
package main
import "fmt"
func main() {
slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar","world"}
diffStr := difference(slice1, slice2)
for _, diffVal := range diffStr {
fmt.Println(diffVal)
}
}
func difference(slice1 []string, slice2 []string) ([]string){
diffStr := []string{}
m :=map [string]int{}
for _, s1Val := range slice1 {
m[s1Val] = 1
}
for _, s2Val := range slice2 {
m[s2Val] = m[s2Val] + 1
}
for mKey, mVal := range m {
if mVal==1 {
diffStr = append(diffStr, mKey)
}
}
return diffStr
}
output:
hello
world
func diff(a, b []string) []string {
temp := map[string]int{}
for _, s := range a {
temp[s]++
}
for _, s := range b {
temp[s]--
}
var result []string
for s, v := range temp {
if v != 0 {
result = append(result, s)
}
}
return result
}
If you want to handle duplicated strings, the v in the map can do that. And you can pick a.Remove(b) ( v>0 ) or b.Remove(a) (v<0)
func unique(slice []string) []string {
encountered := map[string]int{}
diff := []string{}
for _, v := range slice {
encountered[v] = encountered[v]+1
}
for _, v := range slice {
if encountered[v] == 1 {
diff = append(diff, v)
}
}
return diff
}
func main() {
slice1 := []string{"hello", "michael", "dorner"}
slice2 := []string{"hello", "michael"}
slice3 := []string{}
fmt.Println(unique(append(slice1, slice2...))) // [dorner]
fmt.Println(unique(append(slice2, slice3...))) // [michael michael]
}
As mentioned by ANisus, different approaches will suit different sizes of input slices. This solution will work in linear time O(n) independent of input size, but assumes that the "equality" includes index position.
Thus, in the OP's examples of:
slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar"}
The entries foo and bar are equal not just due to value, but also due to their index in the slice.
Given these conditions, you can do something like:
package main
import "fmt"
func difference(s1, s2 []string) string {
var (
lenMin int
longest []string
out string
)
// Determine the shortest length and the longest slice
if len(s1) < len(s2) {
lenMin = len(s1)
longest = s2
} else {
lenMin = len(s2)
longest = s1
}
// compare common indeces
for i := 0; i < lenMin; i++ {
if s1[i] != s2[i] {
out += fmt.Sprintf("=>\t%s\t%s\n", s1[i], s2[i])
}
}
// add indeces not in common
for _, v := range longest[lenMin:] {
out += fmt.Sprintf("=>\t%s\n", v)
}
return out
}
func main() {
slice1 := []string{"foo", "bar", "hello"}
slice2 := []string{"foo", "bar"}
fmt.Print(difference(slice1, slice2))
}
Produces:
=> hello
Playground
If you change the slices to be:
func main() {
slice1 := []string{"foo", "baz", "hello"}
slice2 := []string{"foo", "bar"}
fmt.Print(difference(slice1, slice2))
}
It will produce:
=> baz bar
=> hello
Most of the other solutions here will fail to return the correct answer in case the slices contain duplicated elements.
This solution is O(n) time and O(n) space if the slices are already sorted, and O(n*log(n)) time O(n) space if they are not, but has the nice property of actually being correct. 🤣
func diff(a, b []string) []string {
a = sortIfNeeded(a)
b = sortIfNeeded(b)
var d []string
i, j := 0, 0
for i < len(a) && j < len(b) {
c := strings.Compare(a[i], b[j])
if c == 0 {
i++
j++
} else if c < 0 {
d = append(d, a[i])
i++
} else {
d = append(d, b[j])
j++
}
}
d = append(d, a[i:len(a)]...)
d = append(d, b[j:len(b)]...)
return d
}
func sortIfNeeded(a []string) []string {
if sort.StringsAreSorted(a) {
return a
}
s := append(a[:0:0], a...)
sort.Strings(s)
return s
}
If you know for sure that the slices are already sorted, you can remove the calls to sortIfNeeded (the reason for the defensive slice copy in sortIfNeeded is because sorting is done in-place, so we would be modifying the slices that are passed to diff).
See https://play.golang.org/p/lH-5L0aL1qr for tests showing correctness in face of duplicated entries.
I have this example but it works only for the elements of the first array "not present" in the second array
with generics
type HandleDiff[T comparable] func(item1 T, item2 T) bool
func HandleDiffDefault[T comparable](val1 T, val2 T) bool {
return val1 == val2
}
func Diff[T comparable](items1 []T, items2 []T, callback HandleDiff[T]) []T {
acc := []T{}
for _, item1 := range items1 {
find := false
for _, item2 := range items2 {
if callback(item1, item2) {
find = true
break
}
}
if !find {
acc = append(acc, item1)
}
}
return acc
}
usage
diff := Diff(items1, items2, HandleDiffDefault[string])
Why not keep it simple and use labels?
// returns items unique to slice1
func difference(slice1, slice2 []string) []string {
var diff []string
outer:
for _, v1 := range slice1 {
for _, v2 := range slice2 {
if v1 == v2 {
continue outer
}
}
diff = append(diff, v1)
}
return diff
}
https://go.dev/play/p/H46zSpfocHp
I would add a small change to the solution by #peterwilliams97, so that we can ignore the order of the input.
func difference(a, b []string) []string {
// reorder the input,
// so that we can check the longer slice over the shorter one
longer, shorter := a, b
if len(b) > len(a) {
longer, shorter = b, a
}
mb := make(map[string]struct{}, len(shorter))
for _, x := range shorter {
mb[x] = struct{}{}
}
var diff []string
for _, x := range longer {
if _, found := mb[x]; !found {
diff = append(diff, x)
}
}
return diff
}
The code below gives the absolute difference between strings regardless of the order. Space complexity O(n) and Time complexity O(n).
// difference returns the elements in a that aren't in b
func difference(a, b string) string {
longest, shortest := longestString(&a, &b)
var builder strings.Builder
var mem = make(map[rune]bool)
for _, s := range longest {
mem[s] = true
}
for _, s := range shortest {
if _, ok := mem[s]; ok {
mem[s] = false
}
}
for k, v := range mem {
if v == true {
builder.WriteRune(k)
}
}
return builder.String()
}
func longestString(a *string, b *string) ([]rune, []rune) {
if len(*a) > len(*b) {
return []rune(*a), []rune(*b)
}
return []rune(*b), []rune(*a)
}

Is there a way to iterate over a slice in reverse in Go?

It would be convenient to be able to say something like:
for _, element := reverse range mySlice {
...
}
Edit: I asked this question a long time ago, it is 2022 now and the generic solution by #Ivan below seems like the way to go!
No there is no convenient operator for this to add to the range one in place. You'll have to do a normal for loop counting down:
s := []int{5, 4, 3, 2, 1}
for i := len(s)-1; i >= 0; i-- {
fmt.Println(s[i])
}
You can also do:
s := []int{5, 4, 3, 2, 1}
for i := range s {
fmt.Println(s[len(s)-1-i]) // Suggestion: do `last := len(s)-1` before the loop
}
Output:
1
2
3
4
5
Also here: http://play.golang.org/p/l7Z69TV7Vl
Variation with index
for k := range s {
k = len(s) - 1 - k
// now k starts from the end
}
How about use defer:
s := []int{5, 4, 3, 2, 1}
for i, _ := range s {
defer fmt.Println(s[i])
}
One could use a channel to reverse a list in a function without duplicating it. It makes the code nicer in my sense.
package main
import (
"fmt"
)
func reverse(lst []string) chan string {
ret := make(chan string)
go func() {
for i, _ := range lst {
ret <- lst[len(lst)-1-i]
}
close(ret)
}()
return ret
}
func main() {
elms := []string{"a", "b", "c", "d"}
for e := range reverse(elms) {
fmt.Println(e)
}
}
In 2022, you could use generics to reverse any slice in-place:
func reverse[S ~[]E, E any](s S) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
When I need to extract elements from a slice and reverse range, I use something like this code:
// reverse range
// Go Playground: https://play.golang.org/p/gx6fJIfb7fo
package main
import (
"fmt"
)
type Elem struct {
Id int64
Name string
}
type Elems []Elem
func main() {
mySlice := Elems{{Id: 0, Name: "Alice"}, {Id: 1, Name: "Bob"}, {Id: 2, Name: "Carol"}}
for i, element := range mySlice {
fmt.Printf("Normal range: [%v] %+v\n", i, element)
}
//mySlice = Elems{}
//mySlice = Elems{{Id: 0, Name: "Alice"}}
if last := len(mySlice) - 1; last >= 0 {
for i, element := last, mySlice[0]; i >= 0; i-- {
element = mySlice[i]
fmt.Printf("Reverse range: [%v] %+v\n", i, element)
}
} else {
fmt.Println("mySlice empty")
}
}
Output:
Normal range: [0] {Id:0 Name:Alice}
Normal range: [1] {Id:1 Name:Bob}
Normal range: [2] {Id:2 Name:Carol}
Reverse range: [2] {Id:2 Name:Carol}
Reverse range: [1] {Id:1 Name:Bob}
Reverse range: [0] {Id:0 Name:Alice}
Playground: https://play.golang.org/p/gx6fJIfb7fo
You can use the funk.ForEachRight method from go-funk:
results := []int{}
funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) {
results = append(results, x)
})
fmt.Println(results) // []int{4, 3, 2, 1}

Resources