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?
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.