This commit is contained in:
binwiederhier
2025-07-21 11:24:58 +02:00
parent c807b5db21
commit 50c564d8a2
15 changed files with 1132 additions and 78 deletions

View File

@@ -11,10 +11,15 @@ import (
// ints, and other types not implementing []any can be worked with.
// For example, this is useful if you need to work on the output of regexs.
// list creates a new list (slice) containing the provided arguments.
// It accepts any number of arguments of any type and returns them as a slice.
func list(v ...any) []any {
return v
}
// push appends an element to the end of a list (slice or array).
// It takes a list and a value, and returns a new list with the value appended.
// This function will panic if the first argument is not a slice or array.
func push(list any, v any) []any {
l, err := mustPush(list, v)
if err != nil {
@@ -23,99 +28,103 @@ func push(list any, v any) []any {
return l
}
// mustPush is the implementation of push that returns an error instead of panicking.
// It converts the input list to a slice of any type, then appends the value.
func mustPush(list any, v any) ([]any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
nl := make([]any, l)
for i := 0; i < l; i++ {
nl[i] = l2.Index(i).Interface()
}
return append(nl, v), nil
default:
return nil, fmt.Errorf("cannot push on type %s", tp)
}
}
// prepend adds an element to the beginning of a list (slice or array).
// It takes a list and a value, and returns a new list with the value at the start.
// This function will panic if the first argument is not a slice or array.
func prepend(list any, v any) []any {
l, err := mustPrepend(list, v)
if err != nil {
panic(err)
}
return l
}
// mustPrepend is the implementation of prepend that returns an error instead of panicking.
// It converts the input list to a slice of any type, then prepends the value.
func mustPrepend(list any, v any) ([]any, error) {
//return append([]any{v}, list...)
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
nl := make([]any, l)
for i := 0; i < l; i++ {
nl[i] = l2.Index(i).Interface()
}
return append([]any{v}, nl...), nil
default:
return nil, fmt.Errorf("cannot prepend on type %s", tp)
}
}
// chunk divides a list into sub-lists of the specified size.
// It takes a size and a list, and returns a list of lists, each containing
// up to 'size' elements from the original list.
// This function will panic if the second argument is not a slice or array.
func chunk(size int, list any) [][]any {
l, err := mustChunk(size, list)
if err != nil {
panic(err)
}
return l
}
// mustChunk is the implementation of chunk that returns an error instead of panicking.
// It divides the input list into chunks of the specified size.
func mustChunk(size int, list any) ([][]any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
cs := int(math.Floor(float64(l-1)/float64(size)) + 1)
nl := make([][]any, cs)
for i := 0; i < cs; i++ {
numChunks := int(math.Floor(float64(l-1)/float64(size)) + 1)
if numChunks > sliceSizeLimit {
return nil, fmt.Errorf("number of chunks %d exceeds maximum limit of %d", numChunks, sliceSizeLimit)
}
result := make([][]any, numChunks)
for i := 0; i < numChunks; i++ {
clen := size
if i == cs-1 {
// Handle the last chunk which might be smaller
if i == numChunks-1 {
clen = int(math.Floor(math.Mod(float64(l), float64(size))))
if clen == 0 {
clen = size
}
}
nl[i] = make([]any, clen)
result[i] = make([]any, clen)
for j := 0; j < clen; j++ {
ix := i*size + j
nl[i][j] = l2.Index(ix).Interface()
result[i][j] = l2.Index(ix).Interface()
}
}
return nl, nil
return result, nil
default:
return nil, fmt.Errorf("cannot chunk type %s", tp)
}
}
// last returns the last element of a list (slice or array).
// If the list is empty, it returns nil.
// This function will panic if the argument is not a slice or array.
func last(list any) any {
l, err := mustLast(list)
if err != nil {
@@ -125,6 +134,8 @@ func last(list any) any {
return l
}
// mustLast is the implementation of last that returns an error instead of panicking.
// It returns the last element of the list or nil if the list is empty.
func mustLast(list any) (any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
@@ -142,6 +153,9 @@ func mustLast(list any) (any, error) {
}
}
// first returns the first element of a list (slice or array).
// If the list is empty, it returns nil.
// This function will panic if the argument is not a slice or array.
func first(list any) any {
l, err := mustFirst(list)
if err != nil {
@@ -151,6 +165,8 @@ func first(list any) any {
return l
}
// mustFirst is the implementation of first that returns an error instead of panicking.
// It returns the first element of the list or nil if the list is empty.
func mustFirst(list any) (any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
@@ -168,6 +184,9 @@ func mustFirst(list any) (any, error) {
}
}
// rest returns all elements of a list except the first one.
// If the list is empty, it returns nil.
// This function will panic if the argument is not a slice or array.
func rest(list any) []any {
l, err := mustRest(list)
if err != nil {
@@ -177,28 +196,30 @@ func rest(list any) []any {
return l
}
// mustRest is the implementation of rest that returns an error instead of panicking.
// It returns all elements of the list except the first one, or nil if the list is empty.
func mustRest(list any) ([]any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
if l == 0 {
return nil, nil
}
nl := make([]any, l-1)
for i := 1; i < l; i++ {
nl[i-1] = l2.Index(i).Interface()
}
return nl, nil
default:
return nil, fmt.Errorf("cannot find rest on type %s", tp)
}
}
// initial returns all elements of a list except the last one.
// If the list is empty, it returns nil.
// This function will panic if the argument is not a slice or array.
func initial(list any) []any {
l, err := mustInitial(list)
if err != nil {
@@ -208,28 +229,30 @@ func initial(list any) []any {
return l
}
// mustInitial is the implementation of initial that returns an error instead of panicking.
// It returns all elements of the list except the last one, or nil if the list is empty.
func mustInitial(list any) ([]any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
if l == 0 {
return nil, nil
}
nl := make([]any, l-1)
for i := 0; i < l-1; i++ {
nl[i] = l2.Index(i).Interface()
}
return nl, nil
default:
return nil, fmt.Errorf("cannot find initial on type %s", tp)
}
}
// sortAlpha sorts a list of strings alphabetically.
// If the input is not a slice or array, it returns a single-element slice
// containing the string representation of the input.
func sortAlpha(list any) []string {
k := reflect.Indirect(reflect.ValueOf(list)).Kind()
switch k {
@@ -242,6 +265,8 @@ func sortAlpha(list any) []string {
return []string{strval(list)}
}
// reverse returns a new list with the elements in reverse order.
// This function will panic if the argument is not a slice or array.
func reverse(v any) []any {
l, err := mustReverse(v)
if err != nil {
@@ -251,42 +276,45 @@ func reverse(v any) []any {
return l
}
// mustReverse is the implementation of reverse that returns an error instead of panicking.
// It returns a new list with the elements in reverse order.
func mustReverse(v any) ([]any, error) {
tp := reflect.TypeOf(v).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(v)
l := l2.Len()
// We do not sort in place because the incoming array should not be altered.
nl := make([]any, l)
for i := 0; i < l; i++ {
nl[l-i-1] = l2.Index(i).Interface()
}
return nl, nil
default:
return nil, fmt.Errorf("cannot find reverse on type %s", tp)
}
}
// compact returns a new list with all "empty" elements removed.
// An element is considered empty if it's nil, zero, an empty string, or an empty collection.
// This function will panic if the argument is not a slice or array.
func compact(list any) []any {
l, err := mustCompact(list)
if err != nil {
panic(err)
}
return l
}
// mustCompact is the implementation of compact that returns an error instead of panicking.
// It returns a new list with all "empty" elements removed.
func mustCompact(list any) ([]any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
nl := []any{}
var nl []any
var item any
for i := 0; i < l; i++ {
item = l2.Index(i).Interface()
@@ -294,30 +322,32 @@ func mustCompact(list any) ([]any, error) {
nl = append(nl, item)
}
}
return nl, nil
default:
return nil, fmt.Errorf("cannot compact on type %s", tp)
}
}
// uniq returns a new list with duplicate elements removed.
// The first occurrence of each element is kept.
// This function will panic if the argument is not a slice or array.
func uniq(list any) []any {
l, err := mustUniq(list)
if err != nil {
panic(err)
}
return l
}
// mustUniq is the implementation of uniq that returns an error instead of panicking.
// It returns a new list with duplicate elements removed.
func mustUniq(list any) ([]any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
dest := []any{}
var dest []any
var item any
for i := 0; i < l; i++ {
item = l2.Index(i).Interface()
@@ -325,13 +355,15 @@ func mustUniq(list any) ([]any, error) {
dest = append(dest, item)
}
}
return dest, nil
default:
return nil, fmt.Errorf("cannot find uniq on type %s", tp)
}
}
// inList checks if a value is present in a list.
// It uses deep equality comparison to check for matches.
// Returns true if the value is found, false otherwise.
func inList(haystack []any, needle any) bool {
for _, h := range haystack {
if reflect.DeepEqual(needle, h) {
@@ -341,21 +373,23 @@ func inList(haystack []any, needle any) bool {
return false
}
// without returns a new list with all occurrences of the specified values removed.
// This function will panic if the first argument is not a slice or array.
func without(list any, omit ...any) []any {
l, err := mustWithout(list, omit...)
if err != nil {
panic(err)
}
return l
}
// mustWithout is the implementation of without that returns an error instead of panicking.
// It returns a new list with all occurrences of the specified values removed.
func mustWithout(list any, omit ...any) ([]any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
res := []any{}
var item any
@@ -365,22 +399,25 @@ func mustWithout(list any, omit ...any) ([]any, error) {
res = append(res, item)
}
}
return res, nil
default:
return nil, fmt.Errorf("cannot find without on type %s", tp)
}
}
// has checks if a value is present in a list.
// Returns true if the value is found, false otherwise.
// This function will panic if the second argument is not a slice or array.
func has(needle any, haystack any) bool {
l, err := mustHas(needle, haystack)
if err != nil {
panic(err)
}
return l
}
// mustHas is the implementation of has that returns an error instead of panicking.
// It checks if a value is present in a list.
func mustHas(needle any, haystack any) (bool, error) {
if haystack == nil {
return false, nil
@@ -397,38 +434,41 @@ func mustHas(needle any, haystack any) (bool, error) {
return true, nil
}
}
return false, nil
default:
return false, fmt.Errorf("cannot find has on type %s", tp)
}
}
// slice extracts a portion of a list based on the provided indices.
// Usage examples:
// $list := [1, 2, 3, 4, 5]
// slice $list -> list[0:5] = list[:]
// slice $list 0 3 -> list[0:3] = list[:3]
// slice $list 3 5 -> list[3:5]
// slice $list 3 -> list[3:5] = list[3:]
//
// This function will panic if the first argument is not a slice or array.
func slice(list any, indices ...any) any {
l, err := mustSlice(list, indices...)
if err != nil {
panic(err)
}
return l
}
// mustSlice is the implementation of slice that returns an error instead of panicking.
// It extracts a portion of a list based on the provided indices.
func mustSlice(list any, indices ...any) (any, error) {
tp := reflect.TypeOf(list).Kind()
switch tp {
case reflect.Slice, reflect.Array:
l2 := reflect.ValueOf(list)
l := l2.Len()
if l == 0 {
return nil, nil
}
// Determine start and end indices
var start, end int
if len(indices) > 0 {
start = toInt(indices[0])
@@ -438,13 +478,15 @@ func mustSlice(list any, indices ...any) (any, error) {
} else {
end = toInt(indices[1])
}
return l2.Slice(start, end).Interface(), nil
default:
return nil, fmt.Errorf("list should be type of slice or array but %s", tp)
}
}
// concat combines multiple lists into a single list.
// It takes any number of lists and returns a new list containing all elements.
// This function will panic if any argument is not a slice or array.
func concat(lists ...any) any {
var res []any
for _, list := range lists {