Files
phishingclub/backend/vendor/github.com/enetx/g/slice.go
T
Ronni Skansing 07c8adaf76 update vendor deps
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
2025-11-06 23:31:08 +01:00

1087 lines
29 KiB
Go

package g
import (
"bytes"
"errors"
"fmt"
"slices"
"strconv"
"strings"
"github.com/enetx/g/cmp"
"github.com/enetx/g/f"
"github.com/enetx/g/rand"
"github.com/enetx/iter"
)
// NewSlice creates a new Slice of the given generic type T with the specified length and
// capacity.
// The size variadic parameter can have zero, one, or two integer values.
// If no values are provided, an empty Slice with a length and capacity of 0 is returned.
// If one value is provided, it sets both the length and capacity of the Slice.
// If two values are provided, the first value sets the length and the second value sets the
// capacity.
//
// Parameters:
//
// - size ...Int: A variadic parameter specifying the length and/or capacity of the Slice
//
// Returns:
//
// - Slice[T]: A new Slice of the specified generic type T with the given length and capacity
//
// Example usage:
//
// s1 := g.NewSlice[int]() // Creates an empty Slice of type int
// s2 := g.NewSlice[int](5) // Creates an Slice with length and capacity of 5
// s3 := g.NewSlice[int](3, 10) // Creates an Slice with length of 3 and capacity of 10
func NewSlice[T any](size ...Int) Slice[T] {
var (
length Int
capacity Int
)
switch {
case len(size) > 1:
length, capacity = size[0], size[1]
case len(size) == 1:
length, capacity = size[0], size[0]
}
return make(Slice[T], length, capacity)
}
// TransformSlice applies the given function to each element of a Slice and returns a new Slice
// containing the transformed values.
//
// Parameters:
//
// - sl: The input Slice.
//
// - fn: The function to apply to each element of the input Slice.
//
// Returns:
//
// A new Slice containing the results of applying the function to each element of the input Slice.
func TransformSlice[T, U any](sl Slice[T], fn func(T) U) Slice[U] {
if len(sl) == 0 {
return NewSlice[U]()
}
result := make(Slice[U], len(sl))
for i, v := range sl {
result[i] = fn(v)
}
return result
}
// SliceOf creates a new generic slice containing the provided elements.
func SliceOf[T any](slice ...T) Slice[T] { return slice }
// ToHeap converts the slice to a min/max heap with the specified comparison function.
//
// The comparison function should return:
// - cmp.Less if a < b (for min heap)
// - cmp.Greater if a > b (for max heap)
// - cmp.Equal if a == b
//
// Example usage:
//
// slice := g.SliceOf(5, 2, 8, 1, 9)
//
// minHeap := slice.ToHeap(cmp.Cmp[int]) // Min heap: Pop() returns smallest
// maxHeap := slice.ToHeap(func(a, b int) cmp.Ordering {
// return cmp.Cmp(b, a)
// }) // Max heap: Pop() returns largest
//
// Time complexity: O(n)
// Space complexity: O(n) - creates a copy of the slice
func (sl Slice[T]) ToHeap(compareFn func(T, T) cmp.Ordering) *Heap[T] {
if compareFn == nil {
panic("compareFn cannot be nil")
}
h := &Heap[T]{
data: make(Slice[T], len(sl)),
cmp: compareFn,
}
copy(h.data, sl)
h.heapify()
return h
}
// Transform applies a transformation function to the Slice and returns the result.
func (sl Slice[T]) Transform(fn func(Slice[T]) Slice[T]) Slice[T] { return fn(sl) }
// Iter returns an iterator (SeqSlice[T]) for the Slice, allowing for sequential iteration
// over its elements. It is commonly used in combination with higher-order functions,
// such as 'ForEach', to perform operations on each element of the Slice.
//
// Returns:
//
// A SeqSlice[T], which can be used for sequential iteration over the elements of the Slice.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// iterator := slice.Iter()
// iterator.ForEach(func(element int) {
// // Perform some operation on each element
// fmt.Println(element)
// })
//
// The 'Iter' method provides a convenient way to traverse the elements of a Slice
// in a functional style, enabling operations like mapping or filtering.
func (sl Slice[T]) Iter() SeqSlice[T] { return SeqSlice[T](iter.FromSlice(sl)) }
// IterReverse returns an iterator (SeqSlice[T]) for the Slice that allows for sequential iteration
// over its elements in reverse order. This method is useful when you need to traverse the elements
// from the end to the beginning.
//
// Returns:
//
// A SeqSlice[T], which can be used for sequential iteration over the elements of the Slice in reverse order.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// iterator := slice.IterReverse()
// iterator.ForEach(func(element int) {
// // Perform some operation on each element in reverse order
// fmt.Println(element)
// })
//
// The 'IterReverse' method enhances the functionality of the Slice by providing an alternative
// way to iterate through its elements, enhancing flexibility in how data within a Slice is accessed and manipulated.
func (sl Slice[T]) IterReverse() SeqSlice[T] { return SeqSlice[T](iter.FromSliceReverse(sl)) }
// AsAny converts each element of the slice to the 'any' type.
// It returns a new slice containing the elements as 'any' g.Slice[any].
//
// Note: AsAny is useful when you want to work with a slice of a specific type as a slice of 'any'.
// It can be particularly handy in conjunction with Flatten to work with nested slices of different types.
func (sl Slice[T]) AsAny() Slice[any] {
if sl.Empty() {
return NewSlice[any]()
}
result := make(Slice[any], len(sl))
for i, v := range sl {
result[i] = any(v)
}
return result
}
// Fill fills the slice with the specified value.
// This function is useful when you want to create an Slice with all elements having the same
// value.
// This method modifies the original slice in place.
//
// Parameters:
//
// - val T: The value to fill the Slice with.
//
// Returns:
//
// - Slice[T]: A reference to the original Slice filled with the specified value.
//
// Example usage:
//
// slice := g.Slice[int]{0, 0, 0}
// slice.Fill(5)
//
// The modified slice will now contain: 5, 5, 5.
func (sl Slice[T]) Fill(val T) {
if len(sl) == 0 {
return
}
if len(sl) > 32 {
sl[0] = val
for i := 1; i < len(sl); i <<= 1 {
copy(sl[i:], sl[:i])
}
} else {
for i := range sl {
sl[i] = val
}
}
}
// Index returns the index of the first occurrence of the specified value in the slice, or -1 if
// not found.
func (sl Slice[T]) Index(val T) Int {
var zero T
if f.IsComparable(zero) {
return sl.IndexBy(func(v T) bool { return f.Eq[any](v)(val) })
}
return sl.IndexBy(f.Eqd(val))
}
// IndexBy returns the index of the first element in the slice
// satisfying the custom comparison function provided by the user.
// It iterates through the slice and applies the comparison function to each element and the target value.
// If the comparison function returns true for any pair of elements, it returns the index of that element.
// If no such element is found, it returns -1.
func (sl Slice[T]) IndexBy(fn func(t T) bool) Int { return Int(slices.IndexFunc(sl, fn)) }
// RandomSample returns a new slice containing a random sample of elements from the original slice.
// The sampling is done without replacement, meaning that each element can only appear once in the result.
//
// Parameters:
//
// - sequence int: The number of unique elements to include in the random sample.
//
// Returns:
//
// - Slice[T]: A new Slice containing the random sample of unique elements.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5, 6, 7, 8, 9}
// sample := slice.RandomSample(3)
//
// The resulting sample will contain 3 unique elements randomly selected from the original slice.
func (sl Slice[T]) RandomSample(sequence Int) Slice[T] {
if sequence >= sl.Len() {
return sl.Clone()
}
if sequence <= 0 {
return Slice[T]{}
}
n := sl.Len()
if Float(sequence) < Float(n)*0.25 {
result := make(Slice[T], sequence)
swapped := make(map[Int]Int, sequence)
for i := range sequence {
j := i + rand.N(n-i)
vi, foundI := swapped[i]
if !foundI {
vi = i
}
vj, foundJ := swapped[j]
if !foundJ {
vj = j
}
swapped[i] = vj
if i != j {
swapped[j] = vi
}
result[i] = sl[vj]
}
return result
}
result := sl.Clone()
result.Shuffle()
return result[:sequence]
}
// RandomRange returns a new slice containing a random sample of elements from a subrange of the original slice.
// The sampling is done without replacement, meaning that each element can only appear once in the result.
func (sl Slice[T]) RandomRange(from, to Int) Slice[T] {
if from < 0 {
from = 0
}
if to < 0 || to > sl.Len() {
to = sl.Len()
}
if from > to {
from = to
}
return sl.RandomSample(from.RandomRange(to))
}
// Insert inserts values at the specified index in the slice and modifies the original
// slice.
//
// Parameters:
//
// - i Int: The index at which to insert the new values.
//
// - values ...T: A variadic list of values to insert at the specified index.
//
// Example usage:
//
// slice := g.Slice[string]{"a", "b", "c", "d"}
// slice.Insert(2, "e", "f")
//
// The resulting slice will be: ["a", "b", "e", "f", "c", "d"].
func (sl *Slice[T]) Insert(i Int, values ...T) {
if sl.Empty() {
if i != 0 {
panic(Errorf("runtime error: slice bounds out of range [{}] with length 0", i))
}
sl.Push(values...)
return
}
sl.Replace(i, i, values...)
}
// Replace replaces the elements of sl[i:j] with the given values,
// and modifies the original slice in place. Replace panics if sl[i:j]
// is not a valid slice of sl.
//
// Parameters:
//
// - i int: The starting index of the slice to be replaced.
//
// - j int: The ending index of the slice to be replaced.
//
// - values ...T: A variadic list of values to replace the existing slice.
//
// Example usage:
//
// slice := g.Slice[string]{"a", "b", "c", "d"}
// slice.Replace(1, 3, "e", "f")
//
// After the Replace operation, the resulting slice will be: ["a", "e", "f", "d"].
func (sl *Slice[T]) Replace(i, j Int, values ...T) {
ii := sl.bound(i)
jj := sl.bound(j)
if ii.IsErr() {
panic(ii.err)
}
if jj.IsErr() {
panic(jj.err)
}
i, j = ii.v, jj.v
if i > j {
*sl = (*sl)[:0]
return
}
oldLen := sl.Len()
removedCount := j - i
addedCount := Int(len(values))
newLen := oldLen - removedCount + addedCount
if i == j {
if addedCount == 0 {
return
}
if newLen > sl.Cap() {
newSlice := make(Slice[T], newLen)
copy(newSlice[:i], (*sl)[:i])
copy(newSlice[i:i+addedCount], values)
copy(newSlice[i+addedCount:], (*sl)[i:])
*sl = newSlice
} else {
*sl = (*sl)[:newLen]
copy((*sl)[i+addedCount:], (*sl)[i:oldLen])
copy((*sl)[i:], values)
}
return
}
if newLen > sl.Cap() {
newSlice := make(Slice[T], newLen)
copy(newSlice[:i], (*sl)[:i])
copy(newSlice[i:i+addedCount], values)
copy(newSlice[i+addedCount:], (*sl)[j:])
*sl = newSlice
} else {
if newLen != oldLen {
*sl = (*sl)[:newLen]
}
if addedCount != removedCount {
copy((*sl)[i+addedCount:], (*sl)[j:oldLen])
}
copy((*sl)[i:], values)
}
}
// Get returns the element at the given index, handling negative indices as counting from the end
// of the slice.
func (sl Slice[T]) Get(index Int) Option[T] {
i := sl.bound(index)
if i.IsErr() {
return None[T]()
}
return Some(sl[i.v])
}
// Shuffle shuffles the elements in the slice randomly.
// This method modifies the original slice in place.
//
// The function uses the crypto/rand package to generate random indices.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// slice.Shuffle()
// fmt.Println(slice)
//
// Output: A randomly shuffled version of the original slice, e.g., [4 1 5 2 3].
func (sl Slice[T]) Shuffle() {
for i := sl.Len() - 1; i > 0; i-- {
j := rand.N(i + 1)
sl.swap(i, j)
}
}
// Reverse reverses the order of the elements in the slice.
// This method modifies the original slice in place.
//
// Returns:
//
// - Slice[T]: The modified slice with the elements reversed.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// slice.Reverse()
// fmt.Println(slice)
//
// Output: [5 4 3 2 1].
func (sl Slice[T]) Reverse() { slices.Reverse(sl) }
// SortBy sorts the elements in the slice using the provided comparison function.
// It modifies the original slice in place. It requires the elements to be of a type
// that is comparable.
//
// The function takes a custom comparison function as an argument and sorts the elements
// of the slice using the provided logic. The comparison function should return true if
// the element at index i should come before the element at index j, and false otherwise.
//
// Parameters:
//
// - f func(a, b T) cmp.Ordered: A comparison function that takes two indices i and j and returns a bool.
//
// Example usage:
//
// sl := NewSlice[int](1, 5, 3, 2, 4)
// sl.SortBy(func(a, b int) cmp.Ordering { return cmp.Cmp(a, b) }) // sorts in ascending order.
func (sl Slice[T]) SortBy(fn func(a, b T) cmp.Ordering) {
slices.SortFunc(sl, func(a, b T) int { return int(fn(a, b)) })
}
// IsSortedBy checks if the slice is sorted according to the provided comparison function.
//
// The function takes a custom comparison function as an argument and checks if the elements
// are sorted according to the provided logic.
//
// Parameters:
//
// - fn func(a, b T) cmp.Ordering: A comparison function that defines the sort order.
//
// Returns:
//
// - bool: true if the slice is sorted according to the comparison function, false otherwise.
//
// Example usage:
//
// sl := g.SliceOf(1, 2, 3, 4, 5)
// sorted := sl.IsSortedBy(func(a, b int) cmp.Ordering { return cmp.Cmp(a, b) }) // returns true
func (sl Slice[T]) IsSortedBy(fn func(a, b T) cmp.Ordering) bool {
if len(sl) <= 1 {
return true
}
for i := 1; i < len(sl); i++ {
if fn(sl[i-1], sl[i]).IsGt() {
return false
}
}
return true
}
// ToStringSlice converts the Slice into a slice of strings.
func (sl Slice[T]) ToStringSlice() []string {
if len(sl) == 0 {
return nil
}
result := make([]string, len(sl))
for i, v := range sl {
switch val := any(v).(type) {
case String:
result[i] = val.Std()
case Int:
result[i] = strconv.FormatInt(int64(val), 10)
case Float:
result[i] = strconv.FormatFloat(float64(val), 'g', -1, 64)
case Bytes:
result[i] = string(val)
case string:
result[i] = val
case int:
result[i] = strconv.Itoa(val)
case int8:
result[i] = strconv.FormatInt(int64(val), 10)
case int16:
result[i] = strconv.FormatInt(int64(val), 10)
case int32:
result[i] = strconv.FormatInt(int64(val), 10)
case int64:
result[i] = strconv.FormatInt(val, 10)
case uint:
result[i] = strconv.FormatUint(uint64(val), 10)
case uint8:
result[i] = strconv.FormatUint(uint64(val), 10)
case uint16:
result[i] = strconv.FormatUint(uint64(val), 10)
case uint32:
result[i] = strconv.FormatUint(uint64(val), 10)
case uint64:
result[i] = strconv.FormatUint(val, 10)
case float32:
result[i] = strconv.FormatFloat(float64(val), 'g', -1, 32)
case float64:
result[i] = strconv.FormatFloat(val, 'g', -1, 64)
case bool:
result[i] = strconv.FormatBool(val)
default:
if stringer, ok := any(v).(fmt.Stringer); ok {
result[i] = stringer.String()
} else {
result[i] = fmt.Sprint(v)
}
}
}
return result
}
// Join joins the elements in the slice into a single String, separated by the provided separator (if any).
func (sl Slice[T]) Join(sep ...T) String {
if sl.Empty() {
return ""
}
if s, ok := any(sl).(Slice[Bytes]); ok {
var separator Bytes
if len(sep) != 0 {
separator, _ = any(sep[0]).(Bytes)
}
return String(bytes.Join(TransformSlice(s, func(b Bytes) []byte { return b }), separator))
}
if s, ok := any(sl).(Slice[String]); ok {
var separator string
if len(sep) != 0 {
if sepStr, ok := any(sep[0]).(String); ok {
separator = sepStr.Std()
} else {
separator = fmt.Sprint(sep[0])
}
}
strs := make([]string, len(s))
for i, str := range s {
strs[i] = str.Std()
}
return String(strings.Join(strs, separator))
}
var separator string
if len(sep) != 0 {
separator = fmt.Sprint(sep[0])
}
return String(strings.Join(sl.ToStringSlice(), separator))
}
// SubSlice returns a new slice containing elements from the current slice between the specified start
// and end indices, with an optional step parameter to define the increment between elements.
// The function checks if the start and end indices are within the bounds of the original slice.
// If the end index is negative, it represents the position from the end of the slice.
// If the start index is negative, it represents the position from the end of the slice counted
// from the start index.
//
// Parameters:
//
// - start (Int): The start index of the range.
//
// - end (Int): The end index of the range.
//
// - step (Int, optional): The increment between elements. Defaults to 1 if not provided.
// If negative, the slice is traversed in reverse order.
//
// Returns:
//
// - Slice[T]: A new slice containing elements from the current slice between the start and end
// indices, with the specified step.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5, 6, 7, 8, 9}
// subSlice := slice.SubSlice(1, 7, 2) // Extracts elements 2, 4, 6
// fmt.Println(subSlice)
//
// Output: [2 4 6].
func (sl Slice[T]) SubSlice(start, end Int, step ...Int) Slice[T] {
if sl.Empty() {
return sl
}
_step := Slice[Int](step).Get(0).UnwrapOr(1)
ii := sl.bound(start, struct{}{})
jj := sl.bound(end, struct{}{})
if ii.IsErr() {
panic(ii.err)
}
if jj.IsErr() {
panic(jj.err)
}
start, end = ii.v, jj.v
if _step == 1 {
if start >= end {
return NewSlice[T]()
}
return slices.Clone(sl[start:end])
}
if (start >= end && _step > 0) || (start <= end && _step < 0) || _step == 0 {
return NewSlice[T]()
}
var resultSize Int
if _step > 0 {
resultSize = (end - start + _step - 1) / _step
} else {
resultSize = (start - end + (-_step) - 1) / (-_step)
}
slice := make(Slice[T], 0, resultSize)
var loopCondition func(Int) bool
if _step > 0 {
loopCondition = func(i Int) bool { return i < end }
} else {
loopCondition = func(i Int) bool { return i > end }
}
for i := start; loopCondition(i); i += _step {
slice = append(slice, sl[i])
}
return slice
}
// Random returns a random element from the slice.
//
// The function uses the crypto/rand package to generate a random index within the bounds of the
// slice. If the slice is empty, the zero value of type T is returned.
//
// Returns:
//
// - T: A random element from the slice.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// randomElement := slice.Random()
// fmt.Println(randomElement)
//
// Output: <any random element from the slice>.
func (sl Slice[T]) Random() T {
if sl.Empty() {
var zero T
return zero
}
return sl[rand.N(sl.Len())]
}
// Clone returns a copy of the slice.
func (sl Slice[T]) Clone() Slice[T] {
if sl.Empty() {
return NewSlice[T]()
}
return slices.Clone(sl)
}
// LastIndex returns the last index of the slice.
func (sl Slice[T]) LastIndex() Int {
if sl.NotEmpty() {
return sl.Len() - 1
}
return 0
}
// Eq returns true if the slice is equal to the provided other slice.
func (sl Slice[T]) Eq(other Slice[T]) bool {
var zero T
if f.IsComparable(zero) {
return sl.EqBy(other, func(x, y T) bool { return f.Eq[any](x)(y) })
}
return sl.EqBy(other, func(x, y T) bool { return f.Eqd(x)(y) })
}
// EqBy reports whether two slices are equal using an equality
// function on each pair of elements. If the lengths are different,
// EqBy returns false. Otherwise, the elements are compared in
// increasing index order, and the comparison stops at the first index
// for which eq returns false.
func (sl Slice[T]) EqBy(other Slice[T], fn func(x, y T) bool) bool {
return slices.EqualFunc(sl, other, fn)
}
// String returns a string representation of the slice.
func (sl Slice[T]) String() string {
if len(sl) == 0 {
return "Slice[]"
}
var b Builder
b.WriteString("Slice[")
for i, v := range sl {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(Format("{}", v))
}
b.WriteString("]")
return b.String().Std()
}
// Append appends the provided elements to the slice and returns the modified slice.
func (sl Slice[T]) Append(elems ...T) Slice[T] { return append(sl, elems...) }
// AppendUnique appends unique elements from the provided arguments to the current slice.
//
// The function iterates over the provided elements and checks if they are already present
// in the slice. If an element is not already present, it is appended to the slice. The
// resulting slice is returned, containing the unique elements from both the original
// slice and the provided elements.
//
// Parameters:
//
// - elems (...T): A variadic list of elements to be appended to the slice.
//
// Returns:
//
// - Slice[T]: A new slice containing the unique elements from both the original slice
// and the provided elements.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// slice = slice.AppendUnique(3, 4, 5, 6, 7)
// fmt.Println(slice)
//
// Output: [1 2 3 4 5 6 7].
func (sl Slice[T]) AppendUnique(elems ...T) Slice[T] {
for _, elem := range elems {
if !sl.Contains(elem) {
sl = append(sl, elem)
}
}
return sl
}
// Push appends the provided elements to the slice and modifies the original slice.
func (sl *Slice[T]) Push(elems ...T) { *sl = append(*sl, elems...) }
// PushUnique appends unique elements from the provided arguments to the current slice.
//
// The function iterates over the provided elements and checks if they are already present
// in the slice. If an element is not already present, it is appended to the slice.
//
// Parameters:
//
// - elems (...T): A variadic list of elements to be appended to the slice.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// slice.PushUnique(3, 4, 5, 6, 7)
// fmt.Println(slice)
//
// Output: [1 2 3 4 5 6 7].
func (sl *Slice[T]) PushUnique(elems ...T) {
for _, elem := range elems {
if !sl.Contains(elem) {
sl.Push(elem)
}
}
}
// Cap returns the capacity of the Slice.
func (sl Slice[T]) Cap() Int { return Int(cap(sl)) }
// Contains returns true if the slice contains the provided value.
func (sl Slice[T]) Contains(val T) bool { return sl.Index(val) >= 0 }
// ContainsBy returns true if the slice contains an element that satisfies the provided function fn, false otherwise.
func (sl Slice[T]) ContainsBy(fn func(t T) bool) bool { return sl.IndexBy(fn) >= 0 }
// ContainsAny checks if the Slice contains any element from another Slice.
func (sl Slice[T]) ContainsAny(values ...T) bool {
if sl.Empty() || len(values) == 0 {
return false
}
return slices.ContainsFunc(values, sl.Contains)
}
// ContainsAll checks if the Slice contains all elements from another Slice.
func (sl Slice[T]) ContainsAll(values ...T) bool {
if sl.Empty() || len(values) == 0 {
return len(values) == 0
}
for _, v := range values {
if !sl.Contains(v) {
return false
}
}
return true
}
// Delete removes an element or a range of elements from the Slice in-place.
// It modifies the original Slice by creating two slices: one from the
// beginning of the Slice up to the specified `start` index (exclusive),
// and another from the `end` index (inclusive) to the end of the Slice.
// These two slices are then concatenated to form the modified Slice.
//
// Parameters:
//
// - start (Int): The starting index of the element or range to be removed.
// - end (Int, optional): The end index of the range to be removed.
// If omitted, only the element at the `start` index is removed.
//
// Note:
//
// The function supports negative indices. Negative values are counted from
// the end of the Slice: for example, -1 refers to the last element, -2 to
// the second-to-last, and so on.
func (sl *Slice[T]) Delete(start Int, end ...Int) {
sl.Replace(start, Slice[Int](end).Get(0).UnwrapOr(start+1))
}
// Empty returns true if the slice is empty.
func (sl Slice[T]) Empty() bool { return len(sl) == 0 }
// Last returns the last element of the slice.
func (sl Slice[T]) Last() Option[T] { return sl.Get(-1) }
// Ne returns true if the slice is not equal to the provided other slice.
func (sl Slice[T]) Ne(other Slice[T]) bool { return !sl.Eq(other) }
// NeBy reports whether two slices are not equal using an inequality
// function on each pair of elements. If the lengths are different,
// NeBy returns true. Otherwise, the elements are compared in
// increasing index order, and the comparison stops at the first index
// for which fn returns true.
func (sl Slice[T]) NeBy(other Slice[T], fn func(x, y T) bool) bool { return !sl.EqBy(other, fn) }
// NotEmpty checks if the Slice is not empty.
func (sl Slice[T]) NotEmpty() bool { return !sl.Empty() }
// Pop removes and returns the last element of the slice.
// It mutates the original slice by removing the last element.
// It returns None if the slice is empty.
func (sl *Slice[T]) Pop() Option[T] {
if sl.Len() == 0 {
return None[T]()
}
last := (*sl)[sl.Len()-1]
*sl = (*sl)[:sl.Len()-1]
return Some(last)
}
// Set sets the value at the specified index in the slice and returns the modified slice.
// This method modifies the original slice in place.
//
// Parameters:
//
// - index (Int): The index at which to set the new value.
// - val (T): The new value to be set at the specified index.
//
// Returns:
//
// - Slice[T]: The modified slice with the new value set at the specified index.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// slice.Set(2, 99)
// fmt.Println(slice)
//
// Output: [1 2 99 4 5].
func (sl Slice[T]) Set(index Int, val T) {
i := sl.bound(index)
if i.IsErr() {
panic(i.err)
}
sl[i.v] = val
}
// Len returns the length of the slice.
func (sl Slice[T]) Len() Int { return Int(len(sl)) }
// Swap swaps the elements at the specified indices in the slice.
// This method modifies the original slice in place.
//
// Parameters:
//
// - i (Int): The index of the first element to be swapped.
//
// - j (Int): The index of the second element to be swapped.
//
// Returns:
//
// - Slice[T]: The modified slice with the elements at the specified indices swapped.
//
// Example usage:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// slice.Swap(1, 3)
// fmt.Println(slice)
//
// Output: [1 4 3 2 5].
func (sl Slice[T]) Swap(i, j Int) {
ii := sl.bound(i)
jj := sl.bound(j)
if ii.IsErr() {
panic(ii.err)
}
if jj.IsErr() {
panic(jj.err)
}
sl.swap(ii.v, jj.v)
}
func (sl Slice[T]) swap(i, j Int) { sl[i], sl[j] = sl[j], sl[i] }
// Grow increases the slice's capacity, if necessary, to guarantee space for
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation. If n is negative or too large to
// allocate the memory, Grow panics.
func (sl Slice[T]) Grow(n Int) Slice[T] { return slices.Grow(sl, n.Std()) }
// Clip removes unused capacity from the slice.
func (sl Slice[T]) Clip() Slice[T] { return slices.Clip(sl) }
// Std returns a new slice with the same elements as the Slice[T].
func (sl Slice[T]) Std() []T { return sl }
// Print writes the elements of the Slice to the standard output (console)
// and returns the Slice unchanged.
func (sl Slice[T]) Print() Slice[T] { fmt.Print(sl); return sl }
// Println writes the elements of the Slice to the standard output (console) with a newline
// and returns the Slice unchanged.
func (sl Slice[T]) Println() Slice[T] { fmt.Println(sl); return sl }
// Unpack assigns values of the slice's elements to the variables passed as pointers.
// If the number of variables passed is greater than the length of the slice,
// the function ignores the extra variables.
//
// Parameters:
//
// - vars (...*T): Pointers to variables where the values of the slice's elements will be stored.
//
// Example:
//
// slice := g.Slice[int]{1, 2, 3, 4, 5}
// var a, b, c int
// slice.Unpack(&a, &b, &c)
// fmt.Println(a, b, c) // Output: 1 2 3
func (sl Slice[T]) Unpack(vars ...*T) {
n := min(len(sl), len(vars))
for i := range n {
if vars[i] != nil {
*vars[i] = sl[i]
}
}
}
// MaxBy returns the maximum value in the slice according to the provided comparison function fn.
// It applies fn pairwise to the elements of the slice until it finds the maximum value.
// It returns the maximum value found.
//
// Example:
//
// s := Slice[int]{3, 1, 4, 2, 5}
// maxInt := s.MaxBy(cmp.Cmp)
// fmt.Println(maxInt) // Output: 5
func (sl Slice[T]) MaxBy(fn func(a, b T) cmp.Ordering) T { return cmp.MaxBy(fn, sl...) }
// MinBy returns the minimum value in the slice according to the provided comparison function fn.
// It applies fn pairwise to the elements of the slice until it finds the minimum value.
// It returns the minimum value found.
//
// Example:
//
// s := Slice[int]{3, 1, 4, 2, 5}
// minInt := s.MinBy(cmp.Cmp)
// fmt.Println(minInt) // Output: 1
func (sl Slice[T]) MinBy(fn func(a, b T) cmp.Ordering) T { return cmp.MinBy(fn, sl...) }
func (sl Slice[T]) bound(i Int, subslice ...struct{}) Result[Int] {
if sl.Empty() {
return Err[Int](errors.New("runtime error: slice is empty"))
}
ii := i
if ii < 0 {
ii += sl.Len()
}
var negative Int
if len(subslice) != 0 {
negative = -1
}
if ii > sl.Len() || ii < negative {
return Err[Int](Errorf("runtime error: slice bounds out of range [{}] with length {}", i, len(sl)))
}
return Ok(ii)
}