Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Only leave n elements in slice

Tags:

slice

go

Please see this playground. As you can see I have a slice within a struct. I also have a method which can be used to add a new element to the slice. This works fine.

But now my problem is that I want to extend the method so that it leaves n elements of the slice. So when adding a new element, the "oldest" should be removed and the new one should be added.

How can I do this? Aren't there out-of-the-box packages which I can use?

like image 653
Rogier Lommers Avatar asked Oct 21 '25 04:10

Rogier Lommers


1 Answers

For example,

addimport.go:

package main

import (
    "fmt"
    "time"
)

type Statistics struct {
    LastScan time.Time
    Imports  []Import
}

type Import struct {
    text string
}

func (s *Statistics) AddImport(i Import) {
    // only the last n entries are kept
    const n = 2 // n > 0 and small
    if len(s.Imports) >= n {
        copy(s.Imports, s.Imports[len(s.Imports)-n+1:])
        s.Imports = s.Imports[:n-1]
    }
    s.Imports = append(s.Imports, i)
}

func main() {
    s := Statistics{}
    fmt.Println(len(s.Imports), cap(s.Imports), s.Imports)
    s.AddImport(Import{text: "myText1"})
    s.AddImport(Import{text: "myText2"})
    s.AddImport(Import{text: "myText3"})
    fmt.Println(len(s.Imports), cap(s.Imports), s.Imports)
}

Playground: https://play.golang.org/p/204-uB8Zls

Output:

0 0 []
2 2 [{myText2} {myText3}]

Code should be reasonably efficient. Go has a benchmark package. Here are the benchmark results for the solutions from peterSO, Kaedys, and gonutz.

$ go test -bench=. addimport_test.go
BenchmarkAddImport/PeterSO-4   100000   16145 ns/op      96 B/op     3 allocs/op
BenchmarkAddImport/Kaedys-4     30000   59344 ns/op   32032 B/op   502 allocs/op
BenchmarkAddImport/Gonutz-4     30000   60447 ns/op   32032 B/op   502 allocs/op

addimport_test.go:

package main

import (
    "testing"
    "time"
)

type Statistics struct {
    LastScan time.Time
    Imports  []Import
}

type Import struct {
    text string
}

func (s *Statistics) AddImportPeterSO(i Import) {
    // only the last n entries are kept
    const n = 2 // n > 0 and small
    if len(s.Imports) >= n {
        copy(s.Imports, s.Imports[len(s.Imports)-n+1:])
        s.Imports = s.Imports[:n-1]
    }
    s.Imports = append(s.Imports, i)
}

const numberToKeep = 2

func (s *Statistics) AddImportKaedys(i Import) {
    s.Imports = append(s.Imports, i)
    if len(s.Imports) > numberToKeep {
        s.Imports = s.Imports[len(s.Imports)-numberToKeep:]
    }
}

func (s *Statistics) AddImportGonutz(i Import) {
    s.Imports = append(s.Imports, i)
    const max = 2
    if len(s.Imports) > max {
        s.Imports = s.Imports[1:]
    }
}

func benchmarkAddImport(b *testing.B, addImport func(*Statistics, Import)) {
    b.ReportAllocs()
    for i := 0; i < b.N; i++ {
        var s Statistics
        for j := 0; j < 1000; j++ {
            addImport(&s, Import{})
        }
    }
}

func BenchmarkAddImport(b *testing.B) {
    b.Run("PeterSO", func(b *testing.B) {
        benchmarkAddImport(b, (*Statistics).AddImportPeterSO)
    })
    b.Run("Kaedys", func(b *testing.B) {
        benchmarkAddImport(b, (*Statistics).AddImportKaedys)
    })
    b.Run("Gonutz", func(b *testing.B) {
        benchmarkAddImport(b, (*Statistics).AddImportGonutz)
    })
}

Playground: https://play.golang.org/p/Q2X_T5Vofe


The general form of this problem is a circular buffer: Circular buffer.

like image 85
peterSO Avatar answered Oct 23 '25 18:10

peterSO



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!