diff --git a/slicesx/doc.go b/slicesx/doc.go new file mode 100644 index 0000000..c7b5619 --- /dev/null +++ b/slicesx/doc.go @@ -0,0 +1,4 @@ +/* +Package slicesx aims to extend functionality of the stdlib [slices] module. +*/ +package slicesx diff --git a/slicesx/funcs.go b/slicesx/funcs.go new file mode 100644 index 0000000..dcefdb6 --- /dev/null +++ b/slicesx/funcs.go @@ -0,0 +1,43 @@ +package slicesx + +/* +Last returns the last item/element in slice s. + +e will be the nil/zero value of the slice element type +if the slice length is 0. +*/ +func Last[S ~[]E, E any](s S) (e E) { + + var idx int = LastIndex(s) + + if idx < 0 { + return + } + + e = s[idx] + + return +} + +/* +LastIndex returns the index of the last element in slice s. + +If s is empty, idx will be -1. +If s is nil, idx will be -2. +*/ +func LastIndex[S ~[]E, E any](s S) (idx int) { + + var l int = len(s) + + idx = -2 + if s != nil { + idx++ + } + if l == 0 { + return + } + + idx = l - 1 + + return +} diff --git a/slicesx/funcs_test.go b/slicesx/funcs_test.go new file mode 100644 index 0000000..c1614b9 --- /dev/null +++ b/slicesx/funcs_test.go @@ -0,0 +1,235 @@ +package slicesx + +import ( + "testing" +) + +func TestLast(t *testing.T) { + + var v any + var e any + var intSl []int + var strSl []string + var boolSl []bool + var anySl []any + + /* + int + */ + // nil + v = Last(intSl) + e = 0 + if v != e { + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + // empty + intSl = make([]int, 0) + v = Last(intSl) + if v != e { // still expect 0 + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + // populated + intSl = []int{0, 1, 2, 3} + v = Last(intSl) + e = 3 + if v != e { + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + + /* + string + */ + // nil + v = Last(strSl) + e = "" + if v != e { + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + // empty + strSl = make([]string, 0) + v = Last(strSl) + if v != e { // still expect "" + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + // populated + strSl = []string{"a", "b", "c"} + v = Last(strSl) + e = "c" + if v != e { + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + + /* + bool + */ + // nil + v = Last(boolSl) + e = false + if v != e { + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + // empty + boolSl = make([]bool, 0) + v = Last(boolSl) + if v != e { // still expect false + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + // populated + boolSl = []bool{true, false, true, false, true} + v = Last(boolSl) + e = true + if v != e { + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + + /* + any/interface{} + */ + // nil + v = Last(anySl) + e = nil + if v != e { + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + // empty + anySl = make([]any, 0) + v = Last(anySl) + if v != e { // still expect nil + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } + // populated + anySl = []any{3, true, "foo", "bar"} + v = Last(anySl) + e = "bar" + if v != e { + t.Errorf("%T %#v, expected %T %#v", v, v, e, e) + } +} + +/* + this test only runs in Go 1.26+; + which lets you new() on values instead of just types. + + commented out until req bumps to 1.26+ in go.mod. +*/ +/* +func TestLastPtr(t *testing.T) { + + var v any + var i *int + var s *string + var b *bool + var ptrIntSl []*int + var ptrStrSl []*string + var ptrBoolSl []*bool + + + // + // int + // + // nil + v = Last(ptrIntSl) + i = nil + if v != i { + t.Errorf("%T %#v, expected %T %#v", v, v, i, i) + } + // empty + ptrIntSl = make([]*int, 0) + v = Last(ptrIntSl) + if v != i { // still expect nil + t.Errorf("%T %#v, expected %T %#v", v, v, i, i) + } + // populated + ptrIntSl = []*int{new(0), new(1), new(2), new(3)} + v = Last(ptrIntSl) + i = ptrIntSl[len(ptrIntSl)-1] + if v != i { + t.Errorf("%T %#v, expected %T %#v", v, v, i, i) + } + + // + // string + // + // nil + v = Last(ptrStrSl) + if v != s { + t.Errorf("%T %#v, expected %T %#v", v, v, s, s) + } + // empty + ptrStrSl = make([]*string, 0) + v = Last(ptrStrSl) + if v != s { // still expect nil + t.Errorf("%T %#v, expected %T %#v", v, v, s, s) + } + // populated + ptrStrSl = []*string{new("a"), new("b"), new("c")} + v = Last(ptrStrSl) + s = ptrStrSl[len(ptrStrSl)-1] + if v != s { + t.Errorf("%T %#v, expected %T %#v", v, v, s, s) + } + + // + // bool + // + // nil + v = Last(ptrBoolSl) + if v != b { + t.Errorf("%T %#v, expected %T %#v", v, v, b, b) + } + // empty + ptrBoolSl = make([]*bool, 0) + v = Last(ptrBoolSl) + if v != b { // still expect nil + t.Errorf("%T %#v, expected %T %#v", v, v, b, b) + } + // populated + ptrBoolSl = []*bool{new(true), new(false), new(true), new(false), new(true)} + v = Last(ptrBoolSl) + b = ptrBoolSl[len(ptrBoolSl)-1] + if v != b { + t.Errorf("%T %#v, expected %T %#v", v, v, b, b) + } +} +*/ + +func TestLastIndex(t *testing.T) { + + var e int + var v int + var s []string + + e = -2 + v = LastIndex(s) + if e != v { + t.Errorf("Got %d, expected %d", v, e) + } + + e = -1 + s = make([]string, 0) + v = LastIndex(s) + if e != v { + t.Errorf("Got %d, expected %d", v, e) + } + + e = 0 + s = []string{"a"} + v = LastIndex(s) + if e != v { + t.Errorf("Got %d, expected %d", v, e) + } + + e = 1 + s = []string{"a", "b"} + v = LastIndex(s) + if e != v { + t.Errorf("Got %d, expected %d", v, e) + } + + e = 2 + s = []string{"a", "b", "c"} + v = LastIndex(s) + if e != v { + t.Errorf("Got %d, expected %d", v, e) + } +} diff --git a/tplx/sprigx/README.html b/tplx/sprigx/README.html index 11db835..82cfeee 100644 --- a/tplx/sprigx/README.html +++ b/tplx/sprigx/README.html @@ -636,7 +636,7 @@ pre.rouge .gs {
Brent Saner
bts@square-r00t.net
-Last rendered 2026-07-13 19:17:39 -0400 +Last rendered 2026-07-14 03:02:23 -0400
Table of Contents
diff --git a/tplx/sprigx/README.md b/tplx/sprigx/README.md index 6cf1fc9..dc1647c 100644 --- a/tplx/sprigx/README.md +++ b/tplx/sprigx/README.md @@ -6,7 +6,7 @@ Brent Saner -Last rendered 2026-07-13 19:17:43 -0400 +Last rendered 2026-07-14 03:02:27 -0400