mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-10-05 06:56:55 +02:00
1035 lines
26 KiB
Go
1035 lines
26 KiB
Go
package g
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/enetx/g/cmp"
|
|
"github.com/enetx/g/f"
|
|
)
|
|
|
|
// Slice is a generic alias for a slice.
|
|
type Slice[T any] []T
|
|
|
|
// 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)
|
|
}
|
|
|
|
// SliceOf creates a new generic slice containing the provided elements.
|
|
func SliceOf[T any](slice ...T) Slice[T] { return slice }
|
|
|
|
// TransformSlice maps a plain Go slice into a Slice through fn. It is the
|
|
// exported bridge between stdlib-shaped results and g containers, shared with
|
|
// the subpackages (rx uses it for its match groups).
|
|
func TransformSlice[T, U any](sl []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
|
|
}
|
|
|
|
// Transform applies a transformation function to the Slice and returns the result.
|
|
func (sl Slice[T]) Transform[U any](fn func(Slice[T]) U) U { return fn(sl) }
|
|
|
|
// Iter returns an iterator (Seq[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 Seq[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() Seq[T] { return Seq[T](seqFromSlice(sl)) }
|
|
|
|
// IterReverse returns an iterator (Seq[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 Seq[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() Seq[T] {
|
|
return func(yield func(T) bool) {
|
|
for _, v := range slices.Backward(sl) {
|
|
if !yield(v) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
// 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 {
|
|
if f.IsComparable[T]() && reflect.TypeFor[T]().Kind() != reflect.Interface {
|
|
target := any(val)
|
|
|
|
for i, v := range sl {
|
|
if any(v) == target {
|
|
return Int(i)
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
return sl.IndexBy(f.Eqd(val))
|
|
}
|
|
|
|
// IndexBy returns the index of the first element in the slice
|
|
// satisfying the predicate function provided by the user.
|
|
// It iterates through the slice and applies the predicate to each element.
|
|
// If the predicate returns true for an element, 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)) }
|
|
|
|
// Insert inserts values at the specified index in the slice and modifies the original
|
|
// slice.
|
|
//
|
|
// Panics if the index is out of range. A negative index counts from the end of
|
|
// the slice; i == Len() appends at the end.
|
|
//
|
|
// 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.IsEmpty() {
|
|
if i != 0 {
|
|
boundpanic(i, 0)
|
|
}
|
|
|
|
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, ok := sl.boundReplace(i)
|
|
if !ok {
|
|
boundpanic(i, len(*sl))
|
|
}
|
|
|
|
jj, ok := sl.boundReplace(j)
|
|
if !ok {
|
|
boundpanic(j, len(*sl))
|
|
}
|
|
|
|
i, j = ii, jj
|
|
|
|
if i > j {
|
|
boundpanic(j, len(*sl))
|
|
}
|
|
|
|
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, ok := sl.bound(index)
|
|
if !ok {
|
|
return None[T]()
|
|
}
|
|
|
|
return Some(sl[i])
|
|
}
|
|
|
|
// Reverse reverses the order of the elements in the slice.
|
|
// This method modifies the original slice in place.
|
|
//
|
|
// 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.
|
|
//
|
|
// The comparison function should return:
|
|
// - cmp.Less if a should come before b
|
|
// - cmp.Greater if a should come after b
|
|
// - cmp.Equal if a and b are considered equal
|
|
//
|
|
// The sort is not guaranteed to be stable: the relative order of elements that
|
|
// compare equal may change.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// - fn func(a, b T) cmp.Ordering: A comparison function reporting the ordering of a relative to b.
|
|
//
|
|
// 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
|
|
}
|
|
|
|
// BinarySearch searches a sorted slice for value using the comparator fn and
|
|
// returns Some(index) if an equal element is found, or None otherwise. The slice
|
|
// must be sorted in ascending order according to fn (see IsSortedBy).
|
|
func (sl Slice[T]) BinarySearch(value T, fn func(a, b T) cmp.Ordering) Option[Int] {
|
|
if i, ok := slices.BinarySearchFunc(sl, value, func(a, b T) int { return int(fn(a, b)) }); ok {
|
|
return Some(Int(i))
|
|
}
|
|
|
|
return None[Int]()
|
|
}
|
|
|
|
// PartitionPoint returns the index of the first element for which pred returns
|
|
// false, assuming the slice is partitioned so that all elements satisfying pred
|
|
// come first. If pred is true for every element, it returns the slice length.
|
|
// Runs in O(log n).
|
|
func (sl Slice[T]) PartitionPoint(pred func(T) bool) Int {
|
|
lo, hi := Int(0), sl.Len()
|
|
for lo < hi {
|
|
mid := (lo + hi) / 2
|
|
if pred(sl[mid]) {
|
|
lo = mid + 1
|
|
} else {
|
|
hi = mid
|
|
}
|
|
}
|
|
|
|
return lo
|
|
}
|
|
|
|
// Retain keeps only the elements for which fn returns true, removing the rest
|
|
// in place while preserving order. It is the in-place counterpart of Deque.Retain.
|
|
func (sl *Slice[T]) Retain(fn func(T) bool) {
|
|
*sl = slices.DeleteFunc(*sl, func(v T) bool { return !fn(v) })
|
|
}
|
|
|
|
// DedupBy removes consecutive elements considered equal by eq, keeping the first
|
|
// of each run, in place. It is the eager, in-place counterpart of the lazy
|
|
// Seq.Dedup. Only adjacent duplicates are removed, so sort first for a
|
|
// global dedup.
|
|
func (sl *Slice[T]) DedupBy(eq func(a, b T) bool) {
|
|
*sl = slices.CompactFunc(*sl, eq)
|
|
}
|
|
|
|
// 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.IsEmpty() {
|
|
return ""
|
|
}
|
|
|
|
if s, ok := any(sl).(Slice[Bytes]); ok {
|
|
if len(s) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var separator Bytes
|
|
if len(sep) != 0 {
|
|
separator, _ = any(sep[0]).(Bytes)
|
|
}
|
|
|
|
total := len(separator) * (len(s) - 1)
|
|
for _, value := range s {
|
|
total += len(value)
|
|
}
|
|
|
|
var builder Builder
|
|
builder.Grow(Int(total))
|
|
for i, value := range s {
|
|
if i > 0 {
|
|
builder.Write(separator)
|
|
}
|
|
builder.Write(value)
|
|
}
|
|
|
|
return builder.String()
|
|
}
|
|
|
|
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])
|
|
}
|
|
}
|
|
|
|
total := len(separator) * (len(s) - 1)
|
|
for _, str := range s {
|
|
total += len(str)
|
|
}
|
|
|
|
var b strings.Builder
|
|
b.Grow(total)
|
|
|
|
for i, str := range s {
|
|
if i > 0 {
|
|
b.WriteString(separator)
|
|
}
|
|
|
|
b.WriteString(str.Std())
|
|
}
|
|
|
|
return String(b.String())
|
|
}
|
|
|
|
var separator string
|
|
if len(sep) != 0 {
|
|
separator = fmt.Sprint(sep[0])
|
|
}
|
|
|
|
var b strings.Builder
|
|
|
|
for i, v := range sl {
|
|
if i > 0 {
|
|
b.WriteString(separator)
|
|
}
|
|
|
|
fmt.Fprint(&b, v)
|
|
}
|
|
|
|
return String(b.String())
|
|
}
|
|
|
|
// 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.
|
|
// Negative start or end indices count from the end of the slice.
|
|
//
|
|
// Panics if start or end is out of range after negative-index resolution.
|
|
//
|
|
// 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.IsEmpty() {
|
|
return NewSlice[T]()
|
|
}
|
|
|
|
_step := Int(1)
|
|
if len(step) > 0 {
|
|
_step = Int(step[0])
|
|
}
|
|
|
|
ii, ok := sl.boundsub(start)
|
|
if !ok {
|
|
boundpanic(start, len(sl))
|
|
}
|
|
|
|
jj, ok := sl.boundsub(end)
|
|
if !ok {
|
|
boundpanic(end, len(sl))
|
|
}
|
|
|
|
start, end = ii, jj
|
|
|
|
// For a negative step the iteration starts AT start and moves down, so a
|
|
// start clamped to len(sl) must begin at the last element (s[100:0:-1]
|
|
// starts at the final index, not one past it).
|
|
if _step < 0 && start == sl.Len() {
|
|
start--
|
|
}
|
|
|
|
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)
|
|
|
|
if _step > 0 {
|
|
for i := start; i < end; i += _step {
|
|
slice = append(slice, sl[i])
|
|
}
|
|
} else {
|
|
for i := start; i > end; i += _step {
|
|
slice = append(slice, sl[i])
|
|
}
|
|
}
|
|
|
|
return slice
|
|
}
|
|
|
|
// Clone returns a copy of the slice.
|
|
func (sl Slice[T]) Clone() Slice[T] {
|
|
if sl.IsEmpty() {
|
|
return NewSlice[T]()
|
|
}
|
|
|
|
return slices.Clone(sl)
|
|
}
|
|
|
|
// LastIndex returns the last index of the slice.
|
|
func (sl Slice[T]) LastIndex() Int {
|
|
if !sl.IsEmpty() {
|
|
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 {
|
|
if len(sl) != len(other) {
|
|
return false
|
|
}
|
|
|
|
if f.IsComparable[T]() && reflect.TypeFor[T]().Kind() != reflect.Interface {
|
|
for i, v := range sl {
|
|
if any(v) != any(other[i]) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
return sl.EqBy(other, func(x, y T) bool { return reflect.DeepEqual(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.Grow(Int(len(sl)) * 8)
|
|
b.WriteString("Slice[")
|
|
|
|
for i, v := range sl {
|
|
if i > 0 {
|
|
b.WriteString(", ")
|
|
}
|
|
|
|
fmt.Fprint(&b, 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] {
|
|
if f.IsComparable[T]() && reflect.TypeFor[T]().Kind() != reflect.Interface {
|
|
set := make(Set[any], len(sl)+len(elems))
|
|
for _, v := range sl {
|
|
set[v] = Unit{}
|
|
}
|
|
|
|
for _, elem := range elems {
|
|
if !set.Contains(elem) {
|
|
sl = append(sl, elem)
|
|
set.Insert(elem)
|
|
}
|
|
}
|
|
|
|
return sl
|
|
}
|
|
|
|
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) {
|
|
if f.IsComparable[T]() && reflect.TypeFor[T]().Kind() != reflect.Interface {
|
|
set := make(Set[any], len(*sl)+len(elems))
|
|
for _, v := range *sl {
|
|
set[v] = Unit{}
|
|
}
|
|
|
|
for _, elem := range elems {
|
|
if !set.Contains(elem) {
|
|
sl.Push(elem)
|
|
set.Insert(elem)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
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.IsEmpty() || len(values) == 0 {
|
|
return false
|
|
}
|
|
|
|
if f.IsComparable[T]() && reflect.TypeFor[T]().Kind() != reflect.Interface {
|
|
set := make(Set[any], len(sl))
|
|
for _, v := range sl {
|
|
set[v] = Unit{}
|
|
}
|
|
|
|
for _, v := range values {
|
|
if set.Contains(v) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
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.IsEmpty() || len(values) == 0 {
|
|
return len(values) == 0
|
|
}
|
|
|
|
if f.IsComparable[T]() && reflect.TypeFor[T]().Kind() != reflect.Interface {
|
|
set := make(Set[any], len(sl))
|
|
for _, v := range sl {
|
|
set[v] = Unit{}
|
|
}
|
|
|
|
for _, v := range values {
|
|
if !set.Contains(v) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
for _, v := range values {
|
|
if !sl.Contains(v) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Remove removes and returns the element at the specified index.
|
|
// Returns None if index is out of bounds.
|
|
// Negative indices are supported: -1 refers to the last element, etc.
|
|
func (sl *Slice[T]) Remove(index Int) Option[T] {
|
|
if sl.IsEmpty() {
|
|
return None[T]()
|
|
}
|
|
|
|
length := sl.Len()
|
|
|
|
if index < 0 {
|
|
index += length
|
|
}
|
|
|
|
if index < 0 || index >= length {
|
|
return None[T]()
|
|
}
|
|
|
|
value := (*sl)[index]
|
|
*sl = append((*sl)[:index], (*sl)[index+1:]...)
|
|
|
|
return Some(value)
|
|
}
|
|
|
|
// IsEmpty returns true if the slice is empty.
|
|
func (sl Slice[T]) IsEmpty() bool { return len(sl) == 0 }
|
|
|
|
// First returns the first element of the slice.
|
|
func (sl Slice[T]) First() Option[T] { return sl.Get(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) }
|
|
|
|
// 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 previous
|
|
// value wrapped in Some. If the index is out of bounds, the slice is left unchanged
|
|
// and None is returned, mirroring Deque.Set.
|
|
// This method modifies the original slice in place. Negative indices count from the end.
|
|
//
|
|
// 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:
|
|
//
|
|
// - Option[T]: The previous value at the index, or None if the index is out of bounds.
|
|
//
|
|
// Example usage:
|
|
//
|
|
// slice := g.Slice[int]{1, 2, 3, 4, 5}
|
|
// old := slice.Set(2, 99) // Some(3)
|
|
// fmt.Println(slice)
|
|
//
|
|
// Output: [1 2 99 4 5].
|
|
func (sl Slice[T]) Set(index Int, val T) Option[T] {
|
|
i, ok := sl.bound(index)
|
|
if !ok {
|
|
return None[T]()
|
|
}
|
|
|
|
old := sl[i]
|
|
sl[i] = val
|
|
|
|
return Some(old)
|
|
}
|
|
|
|
// 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.
|
|
//
|
|
// Panics if either index is out of range. A negative index counts
|
|
// from the end of the slice.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// - i (Int): The index of the first element to be swapped.
|
|
//
|
|
// - j (Int): The index of the second element to be 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, ok := sl.bound(i)
|
|
if !ok {
|
|
boundpanic(i, len(sl))
|
|
}
|
|
|
|
jj, ok := sl.bound(j)
|
|
if !ok {
|
|
boundpanic(j, len(sl))
|
|
}
|
|
|
|
sl[ii], sl[jj] = sl[jj], sl[ii]
|
|
}
|
|
|
|
// 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]
|
|
}
|
|
}
|
|
}
|
|
|
|
func (sl Slice[T]) bound(i Int) (Int, bool) {
|
|
n := sl.Len()
|
|
if n == 0 {
|
|
return 0, false
|
|
}
|
|
|
|
if i < 0 {
|
|
i += n
|
|
}
|
|
|
|
if i >= n || i < 0 {
|
|
return 0, false
|
|
}
|
|
|
|
return i, true
|
|
}
|
|
|
|
// boundReplace resolves an index for Replace, which may legitimately target
|
|
// i == len(sl) to append at the end. Element accessors use bound (strict i < n).
|
|
func (sl Slice[T]) boundReplace(i Int) (Int, bool) {
|
|
n := sl.Len()
|
|
if n == 0 {
|
|
return 0, false
|
|
}
|
|
|
|
if i < 0 {
|
|
i += n
|
|
}
|
|
|
|
if i > n || i < 0 {
|
|
return 0, false
|
|
}
|
|
|
|
return i, true
|
|
}
|
|
|
|
func (sl Slice[T]) boundsub(i Int) (Int, bool) {
|
|
n := sl.Len()
|
|
if n == 0 {
|
|
return 0, false
|
|
}
|
|
|
|
if i < 0 {
|
|
i += n
|
|
}
|
|
|
|
if i > n || i < -1 {
|
|
return 0, false
|
|
}
|
|
|
|
return i, true
|
|
}
|
|
|
|
func boundpanic(index Int, length int) {
|
|
panic(fmt.Sprintf("runtime error: slice bounds out of range [%d] with length %d", index, length))
|
|
}
|