feat(container/garray): add TArray (#4466)

Add TArray[T] for wrapping Array

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Hunk Zhu
2025-10-15 15:08:26 +08:00
committed by GitHub
parent b8e414e125
commit 4226a23a39
4 changed files with 2673 additions and 0 deletions

View File

@ -67,3 +67,51 @@ func quickSortStr(values []string, comparator func(a, b string) int) {
quickSortStr(values[:head], comparator)
quickSortStr(values[head+1:], comparator)
}
// tToAnySlice converts []T to []any
func tToAnySlice[T comparable](values []T) []any {
if values == nil {
return nil
}
anyValues := make([]any, len(values), cap(values))
for k, v := range values {
anyValues[k] = v
}
return anyValues
}
// anyToTSlice is convert []any to []T
func anyToTSlice[T comparable](values []any) []T {
if values == nil {
return nil
}
tValues := make([]T, len(values), cap(values))
for k, v := range values {
tValues[k], _ = v.(T)
}
return tValues
}
// tToAnySlices converts [][]T to [][]any
func tToAnySlices[T comparable](values [][]T) [][]any {
if values == nil {
return nil
}
anyValues := make([][]any, len(values), cap(values))
for k, v := range values {
anyValues[k] = tToAnySlice(v)
}
return anyValues
}
// anyToTSlices converts [][]any to [][]T
func anyToTSlices[T comparable](values [][]any) [][]T {
if values == nil {
return nil
}
tValues := make([][]T, len(values), cap(values))
for k, v := range values {
tValues[k] = anyToTSlice[T](v)
}
return tValues
}

View File

@ -0,0 +1,493 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray
import (
"github.com/gogf/gf/v2/util/gconv"
)
// TArray is a golang array with rich features.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
// TArray is a wrapper of Array. It is designed to make using Array more convenient.
type TArray[T comparable] struct {
Array
}
// NewTArray creates and returns an empty array.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewTArray[T comparable](safe ...bool) *TArray[T] {
return &TArray[T]{
Array: *NewArray(safe...),
}
}
// NewTArraySize create and returns an array with given size and cap.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewTArraySize[T comparable](size int, cap int, safe ...bool) *TArray[T] {
arr := NewArraySize(size, cap, safe...)
ret := &TArray[T]{
Array: *arr,
}
return ret
}
// NewTArrayFrom creates and returns an array with given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewTArrayFrom[T comparable](array []T, safe ...bool) *TArray[T] {
return &TArray[T]{
Array: *NewArrayFrom(tToAnySlice(array), safe...),
}
}
// NewTArrayFromCopy creates and returns an array from a copy of given slice `array`.
// The parameter `safe` is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewTArrayFromCopy[T comparable](array []T, safe ...bool) *TArray[T] {
return &TArray[T]{
Array: *NewArrayFromCopy(tToAnySlice(array), safe...),
}
}
// At returns the value by the specified index.
// If the given `index` is out of range of the array, it returns `nil`.
func (a *TArray[T]) At(index int) (value T) {
value, _ = a.Array.At(index).(T)
return
}
// Get returns the value by the specified index.
// If the given `index` is out of range of the array, the `found` is false.
func (a *TArray[T]) Get(index int) (value T, found bool) {
val, found := a.Array.Get(index)
if !found {
return
}
value, _ = val.(T)
return
}
// Set sets value to specified index.
func (a *TArray[T]) Set(index int, value T) error {
return a.Array.Set(index, value)
}
// SetArray sets the underlying slice array with the given `array`.
func (a *TArray[T]) SetArray(array []T) *TArray[T] {
a.Array.SetArray(tToAnySlice(array))
return a
}
// Replace replaces the array items by given `array` from the beginning of array.
func (a *TArray[T]) Replace(array []T) *TArray[T] {
a.Array.Replace(tToAnySlice(array))
return a
}
// Sum returns the sum of values in an array.
func (a *TArray[T]) Sum() int {
return a.Array.Sum()
}
// SortFunc sorts the array by custom function `less`.
func (a *TArray[T]) SortFunc(less func(v1, v2 T) bool) *TArray[T] {
a.Array.SortFunc(func(v1, v2 any) bool {
v1t, _ := v1.(T)
v2t, _ := v2.(T)
return less(v1t, v2t)
})
return a
}
// InsertBefore inserts the `values` to the front of `index`.
func (a *TArray[T]) InsertBefore(index int, values ...T) error {
return a.Array.InsertBefore(index, tToAnySlice(values)...)
}
// InsertAfter inserts the `values` to the back of `index`.
func (a *TArray[T]) InsertAfter(index int, values ...T) error {
return a.Array.InsertAfter(index, tToAnySlice(values)...)
}
// Remove removes an item by index.
// If the given `index` is out of range of the array, the `found` is false.
func (a *TArray[T]) Remove(index int) (value T, found bool) {
val, found := a.Array.Remove(index)
if !found {
return
}
value, _ = val.(T)
return
}
// RemoveValue removes an item by value.
// It returns true if value is found in the array, or else false if not found.
func (a *TArray[T]) RemoveValue(value T) bool {
return a.Array.RemoveValue(value)
}
// RemoveValues removes multiple items by `values`.
func (a *TArray[T]) RemoveValues(values ...T) {
a.Array.RemoveValues(tToAnySlice(values)...)
}
// PushLeft pushes one or multiple items to the beginning of array.
func (a *TArray[T]) PushLeft(value ...T) *TArray[T] {
a.Array.PushLeft(tToAnySlice(value)...)
return a
}
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *TArray[T]) PushRight(value ...T) *TArray[T] {
a.Array.PushRight(tToAnySlice(value)...)
return a
}
// PopRand randomly pops and return an item out of array.
// Note that if the array is empty, the `found` is false.
func (a *TArray[T]) PopRand() (value T, found bool) {
val, found := a.Array.PopRand()
if !found {
return
}
value, _ = val.(T)
return
}
// PopRands randomly pops and returns `size` items out of array.
func (a *TArray[T]) PopRands(size int) []T {
return anyToTSlice[T](a.Array.PopRands(size))
}
// PopLeft pops and returns an item from the beginning of array.
// Note that if the array is empty, the `found` is false.
func (a *TArray[T]) PopLeft() (value T, found bool) {
val, found := a.Array.PopLeft()
if !found {
return
}
value, _ = val.(T)
return
}
// PopRight pops and returns an item from the end of array.
// Note that if the array is empty, the `found` is false.
func (a *TArray[T]) PopRight() (value T, found bool) {
val, found := a.Array.PopRight()
if !found {
return
}
value, _ = val.(T)
return
}
// PopLefts pops and returns `size` items from the beginning of array.
func (a *TArray[T]) PopLefts(size int) []T {
return anyToTSlice[T](a.Array.PopLefts(size))
}
// PopRights pops and returns `size` items from the end of array.
func (a *TArray[T]) PopRights(size int) []T {
return anyToTSlice[T](a.Array.PopRights(size))
}
// Range picks and returns items by range, like array[start:end].
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// If `end` is negative, then the offset will start from the end of array.
// If `end` is omitted, then the sequence will have everything from start up
// until the end of the array.
func (a *TArray[T]) Range(start int, end ...int) []T {
return anyToTSlice[T](a.Array.Range(start, end...))
}
// SubSlice returns a slice of elements from the array as specified
// by the `offset` and `size` parameters.
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
//
// If offset is non-negative, the sequence will start at that offset in the array.
// If offset is negative, the sequence will start that far from the end of the array.
//
// If length is given and is positive, then the sequence will have up to that many elements in it.
// If the array is shorter than the length, then only the available array elements will be present.
// If length is given and is negative then the sequence will stop that many elements from the end of the array.
// If it is omitted, then the sequence will have everything from offset up until the end of the array.
//
// Any possibility crossing the left border of array, it will fail.
func (a *TArray[T]) SubSlice(offset int, length ...int) []T {
return anyToTSlice[T](a.Array.SubSlice(offset, length...))
}
// Append is alias of PushRight, please See PushRight.
func (a *TArray[T]) Append(value ...T) *TArray[T] {
a.Array.Append(tToAnySlice(value)...)
return a
}
// Len returns the length of array.
func (a *TArray[T]) Len() int {
return a.Array.Len()
}
// Slice returns the underlying data of array.
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (a *TArray[T]) Slice() []T {
return anyToTSlice[T](a.Array.Slice())
}
// Interfaces returns current array as []any.
func (a *TArray[T]) Interfaces() []any {
return a.Array.Interfaces()
}
// Clone returns a new array, which is a copy of current array.
func (a *TArray[T]) Clone() *TArray[T] {
return &TArray[T]{
Array: *a.Array.Clone(),
}
}
// Clear deletes all items of current array.
func (a *TArray[T]) Clear() *TArray[T] {
a.Array.Clear()
return a
}
// Contains checks whether a value exists in the array.
func (a *TArray[T]) Contains(value T) bool {
return a.Array.Contains(value)
}
// Search searches array by `value`, returns the index of `value`,
// or returns -1 if not exists.
func (a *TArray[T]) Search(value T) int {
return a.Array.Search(value)
}
// Unique uniques the array, clear repeated items.
// Example: [1,1,2,3,2] -> [1,2,3]
func (a *TArray[T]) Unique() *TArray[T] {
a.Array.Unique()
return a
}
// LockFunc locks writing by callback function `f`.
func (a *TArray[T]) LockFunc(f func(array []T)) *TArray[T] {
a.Array.LockFunc(func(array []any) {
vals := anyToTSlice[T](array)
f(vals)
for k, v := range vals {
array[k] = v
}
})
return a
}
// RLockFunc locks reading by callback function `f`.
func (a *TArray[T]) RLockFunc(f func(array []T)) *TArray[T] {
a.Array.RLockFunc(func(array []any) {
f(anyToTSlice[T](array))
})
return a
}
// Merge merges `array` into current array.
// The parameter `array` can be any garray or slice type.
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *TArray[T]) Merge(array any) *TArray[T] {
switch v := array.(type) {
case *Array:
return a.Merge(v.Slice())
case *StrArray:
return a.Merge(v.Slice())
case *IntArray:
return a.Merge(v.Slice())
case *TArray[T]:
a.Array.Merge(&v.Array)
case []T:
a.Array.Merge(v)
case TArray[T]:
a.Array.Merge(&v.Array)
default:
var vals []T
if err := gconv.Scan(v, &vals); err != nil {
panic(err)
}
a.Append(vals...)
}
return a
}
// Fill fills an array with num entries of the value `value`,
// keys starting at the `startIndex` parameter.
func (a *TArray[T]) Fill(startIndex int, num int, value T) error {
return a.Array.Fill(startIndex, num, value)
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (a *TArray[T]) Chunk(size int) (values [][]T) {
return anyToTSlices[T](a.Array.Chunk(size))
}
// Pad pads array to the specified length with `value`.
// If size is positive then the array is padded on the right, or negative on the left.
// If the absolute value of `size` is less than or equal to the length of the array
// then no padding takes place.
func (a *TArray[T]) Pad(size int, val T) *TArray[T] {
a.Array.Pad(size, val)
return a
}
// Rand randomly returns one item from array(no deleting).
func (a *TArray[T]) Rand() (value T, found bool) {
val, found := a.Array.Rand()
if !found {
return
}
value, _ = val.(T)
return
}
// Rands randomly returns `size` items from array(no deleting).
func (a *TArray[T]) Rands(size int) []T {
return anyToTSlice[T](a.Array.Rands(size))
}
// Shuffle randomly shuffles the array.
func (a *TArray[T]) Shuffle() *TArray[T] {
a.Array.Shuffle()
return a
}
// Reverse makes array with elements in reverse order.
func (a *TArray[T]) Reverse() *TArray[T] {
a.Array.Reverse()
return a
}
// Join joins array elements with a string `glue`.
func (a *TArray[T]) Join(glue string) string {
return a.Array.Join(glue)
}
// CountValues counts the number of occurrences of all values in the array.
func (a *TArray[T]) CountValues() (valueCnt map[T]int) {
valueCnt = map[T]int{}
for k, v := range a.Array.CountValues() {
k0, _ := k.(T)
valueCnt[k0] = v
}
return
}
// Iterator is alias of IteratorAsc.
func (a *TArray[T]) Iterator(f func(k int, v T) bool) {
a.Array.Iterator(func(k int, v any) bool {
v0, _ := v.(T)
return f(k, v0)
})
}
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *TArray[T]) IteratorAsc(f func(k int, v T) bool) {
a.Array.IteratorAsc(func(k int, v any) bool {
v0, _ := v.(T)
return f(k, v0)
})
}
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
// If `f` returns true, then it continues iterating; or false to stop.
func (a *TArray[T]) IteratorDesc(f func(k int, v T) bool) {
a.Array.IteratorDesc(func(k int, v any) bool {
v0, _ := v.(T)
return f(k, v0)
})
}
// String returns current array as a string, which implements like json.Marshal does.
func (a *TArray[T]) String() string {
if a == nil {
return ""
}
return a.Array.String()
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
// Note that do not use pointer as its receiver here.
func (a TArray[T]) MarshalJSON() ([]byte, error) {
return a.Array.MarshalJSON()
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *TArray[T]) UnmarshalJSON(b []byte) error {
return a.Array.UnmarshalJSON(b)
}
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *TArray[T]) UnmarshalValue(value any) error {
return a.Array.UnmarshalValue(value)
}
// Filter iterates array and filters elements using custom callback function.
// It removes the element from array if callback function `filter` returns true,
// it or else does nothing and continues iterating.
func (a *TArray[T]) Filter(filter func(index int, value T) bool) *TArray[T] {
a.Array.Filter(func(index int, value any) bool {
val, _ := value.(T)
return filter(index, val)
})
return a
}
// FilterNil removes all nil value of the array.
func (a *TArray[T]) FilterNil() *TArray[T] {
a.Array.FilterNil()
return a
}
// FilterEmpty removes all empty value of the array.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (a *TArray[T]) FilterEmpty() *TArray[T] {
a.Array.FilterEmpty()
return a
}
// Walk applies a user supplied function `f` to every item of array.
func (a *TArray[T]) Walk(f func(value T) T) *TArray[T] {
a.Array.Walk(func(value any) any {
val, _ := value.(T)
return f(val)
})
return a
}
// IsEmpty checks whether the array is empty.
func (a *TArray[T]) IsEmpty() bool {
return a.Array.IsEmpty()
}
// DeepCopy implements interface for deep copy of current type.
func (a *TArray[T]) DeepCopy() any {
if a == nil {
return nil
}
arr := a.Array.DeepCopy().(*Array)
return &TArray[T]{
Array: *arr,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,851 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go
package garray_test
import (
"testing"
"time"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gconv"
)
func Test_TArray_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []int{0, 1, 2, 3}
array := garray.NewTArrayFrom(expect)
array2 := garray.NewTArrayFrom(expect)
array3 := garray.NewTArrayFrom([]int{})
t.Assert(array.Slice(), expect)
t.Assert(array.Interfaces(), expect)
err := array.Set(0, 100) // 100, 1, 2, 3
t.AssertNil(err)
err = array.Set(100, 100)
t.AssertNE(err, nil)
t.Assert(array.IsEmpty(), false)
copyArray := array.DeepCopy()
ca := copyArray.(*garray.TArray[int])
ca.Set(0, 1)
cval, _ := ca.Get(0)
val, _ := array.Get(0)
t.AssertNE(cval, val)
v, ok := array.Get(0)
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Get(1)
t.Assert(v, 1)
t.Assert(ok, true)
v, ok = array.Get(4)
t.Assert(v, 0)
t.Assert(ok, false)
t.Assert(array.Search(100), 0)
t.Assert(array3.Search(100), -1)
t.Assert(array.Contains(100), true)
v, ok = array.Remove(0) // 1, 2, 3
t.Assert(v, 100)
t.Assert(ok, true)
v, ok = array.Remove(-1)
t.Assert(v, 0)
t.Assert(ok, false)
v, ok = array.Remove(100000)
t.Assert(v, 0)
t.Assert(ok, false)
v, ok = array2.Remove(3) // 0 1 2
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array2.Remove(1) // 0 2
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Contains(100), false)
array.Append(4) // 1, 2, 3 ,4
t.Assert(array.Len(), 4)
array.InsertBefore(0, 100) // 100, 1, 2, 3, 4
array.InsertAfter(0, 200) // 100, 200, 1, 2, 3, 4
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
t.Assert(array.Clear().Len(), 0)
err = array.InsertBefore(99, 9900)
t.AssertNE(err, nil)
err = array.InsertAfter(99, 9900)
t.AssertNE(err, nil)
t.Assert(array.String(), "[]")
})
}
func TestTArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect1 := []any{0, 1, 2, 3}
expect2 := []any{3, 2, 1, 0}
array := garray.NewTArray[int]()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.SortFunc(func(v1, v2 int) bool {
return v1 < v2
})
t.Assert(array.Slice(), expect1)
array.SortFunc(func(v1, v2 int) bool {
return v1 > v2
})
t.Assert(array.Slice(), expect2)
})
}
func TestTArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array := garray.NewTArrayFrom(expect)
t.Assert(array.Unique().Slice(), []int{1, 2, 3, 4, 5})
})
gtest.C(t, func(t *gtest.T) {
expect := []int{}
array := garray.NewTArrayFrom(expect)
t.Assert(array.Unique().Slice(), []int{})
})
}
func TestTArray_PushAndPop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []any{0, 1, 2, 3}
array := garray.NewTArrayFrom(expect)
t.Assert(array.Slice(), expect)
v, ok := array.PopLeft()
t.Assert(v, 0)
t.Assert(ok, true)
v, ok = array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []any{1, 2})
t.Assert(ok, true)
v, ok = array.PopRand()
t.AssertIN(v, []any{1, 2})
t.Assert(ok, true)
t.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
t.Assert(array.Slice(), []any{1, 2})
})
}
func TestTArray_PopRands(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{100, 200, 300, 400, 500, 600}
array := garray.NewFromCopy(a1)
t.AssertIN(array.PopRands(2), []any{100, 200, 300, 400, 500, 600})
})
}
func TestTArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestTArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestTArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
t.Assert(array.PopLefts(2), g.Slice{1, 2})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{3})
t.Assert(array.Len(), 0)
})
}
func TestTArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
t.Assert(array.PopRights(2), g.Slice{2, 3})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{1})
t.Assert(array.Len(), 0)
})
}
func TestTArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.New()
v, ok := array.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopLefts(10), nil)
v, ok = array.PopRight()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRights(10), nil)
v, ok = array.PopRand()
t.Assert(v, nil)
t.Assert(ok, false)
t.Assert(array.PopRands(10), nil)
})
gtest.C(t, func(t *gtest.T) {
value1 := []any{0, 1, 2, 3, 4, 5, 6}
value2 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(value1)
array2 := garray.NewTArrayFrom(value2)
t.Assert(array1.PopLefts(2), []any{0, 1})
t.Assert(array1.Slice(), []any{2, 3, 4, 5, 6})
t.Assert(array1.PopRights(2), []any{5, 6})
t.Assert(array1.Slice(), []any{2, 3, 4})
t.Assert(array1.PopRights(20), []any{2, 3, 4})
t.Assert(array1.Slice(), []any{})
t.Assert(array2.PopLefts(20), []any{0, 1, 2, 3, 4, 5, 6})
t.Assert(array2.Slice(), []any{})
})
}
func TestTArray_Range(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
value1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(value1)
array2 := garray.NewTArrayFrom(value1, true)
t.Assert(array1.Range(0, 1), []any{0})
t.Assert(array1.Range(1, 2), []any{1})
t.Assert(array1.Range(0, 2), []any{0, 1})
t.Assert(array1.Range(-1, 10), value1)
t.Assert(array1.Range(10, 2), nil)
t.Assert(array2.Range(1, 3), []any{1, 2})
})
}
func TestTArray_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
func1 := func(v1, v2 any) int {
if gconv.Int(v1) < gconv.Int(v2) {
return 0
}
return 1
}
i1 := []any{0, 1, 2, 3}
i2 := []any{4, 5, 6, 7}
array1 := garray.NewTArrayFrom(i1)
array2 := garray.NewTArrayFrom(i2)
t.Assert(array1.Merge(array2).Slice(), []any{0, 1, 2, 3, 4, 5, 6, 7})
// s1 := []string{"a", "b", "c", "d"}
s2 := []string{"e", "f"}
i3 := garray.NewIntArrayFrom([]int{1, 2, 3})
i4 := garray.NewTArrayFrom([]any{3})
s3 := garray.NewStrArrayFrom([]string{"g", "h"})
s4 := garray.NewSortedArrayFrom([]any{4, 5}, func1)
s5 := garray.NewSortedStrArrayFrom(s2)
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
a1 := garray.NewTArrayFrom(i1)
t.Assert(a1.Merge(s2).Len(), 6)
t.Assert(a1.Merge(i3).Len(), 9)
t.Assert(a1.Merge(i4).Len(), 10)
t.Assert(a1.Merge(s3).Len(), 12)
t.Assert(a1.Merge(s4).Len(), 14)
t.Assert(a1.Merge(s5).Len(), 16)
t.Assert(a1.Merge(s6).Len(), 19)
})
}
func TestTArray_Fill(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0}
a2 := []any{0}
array1 := garray.NewTArrayFrom(a1)
array2 := garray.NewTArrayFrom(a2, true)
t.Assert(array1.Fill(1, 2, 100), nil)
t.Assert(array1.Slice(), []any{0, 100, 100})
t.Assert(array2.Fill(0, 2, 100), nil)
t.Assert(array2.Slice(), []any{100, 100})
t.AssertNE(array2.Fill(-1, 2, 100), nil)
t.Assert(array2.Slice(), []any{100, 100})
})
}
func TestTArray_Chunk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{1, 2, 3, 4, 5}
array1 := garray.NewTArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []any{1, 2})
t.Assert(chunks[1], []any{3, 4})
t.Assert(chunks[2], []any{5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{1, 2, 3, 4, 5}
array1 := garray.NewTArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []any{1, 2, 3})
t.Assert(chunks[1], []any{4, 5})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
chunks := array1.Chunk(2)
t.Assert(len(chunks), 3)
t.Assert(chunks[0], []any{1, 2})
t.Assert(chunks[1], []any{3, 4})
t.Assert(chunks[2], []any{5, 6})
t.Assert(array1.Chunk(0), nil)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
chunks := array1.Chunk(3)
t.Assert(len(chunks), 2)
t.Assert(chunks[0], []any{1, 2, 3})
t.Assert(chunks[1], []any{4, 5, 6})
t.Assert(array1.Chunk(0), nil)
})
}
func TestTArray_Pad(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Pad(3, 1).Slice(), []any{0, 1, 1})
t.Assert(array1.Pad(-4, 1).Slice(), []any{1, 0, 1, 1})
t.Assert(array1.Pad(3, 1).Slice(), []any{1, 0, 1, 1})
})
}
func TestTArray_SubSlice(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
array2 := garray.NewTArrayFrom(a1, true)
t.Assert(array1.SubSlice(0, 2), []any{0, 1})
t.Assert(array1.SubSlice(2, 2), []any{2, 3})
t.Assert(array1.SubSlice(5, 8), []any{5, 6})
t.Assert(array1.SubSlice(9, 1), nil)
t.Assert(array1.SubSlice(-2, 2), []any{5, 6})
t.Assert(array1.SubSlice(-9, 2), nil)
t.Assert(array1.SubSlice(1, -2), nil)
t.Assert(array2.SubSlice(0, 2), []any{0, 1})
})
}
func TestTArray_Rand(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(len(array1.Rands(2)), 2)
t.Assert(len(array1.Rands(10)), 10)
t.AssertIN(array1.Rands(1)[0], a1)
})
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewTArrayFrom(s1)
i1, ok := a1.Rand()
t.Assert(ok, true)
t.Assert(a1.Contains(i1), true)
t.Assert(a1.Len(), 4)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{}
array1 := garray.NewTArrayFrom(a1)
rand, found := array1.Rand()
t.AssertNil(rand)
t.Assert(found, false)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{}
array1 := garray.NewTArrayFrom(a1)
rand := array1.Rands(1)
t.AssertNil(rand)
})
}
func TestTArray_Shuffle(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Shuffle().Len(), 7)
})
}
func TestTArray_Reverse(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Reverse().Slice(), []any{6, 5, 4, 3, 2, 1, 0})
})
}
func TestTArray_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, `"a"`, `\a`}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.Join("."), `0.1."a".\a`)
})
gtest.C(t, func(t *gtest.T) {
a1 := []any{}
array1 := garray.NewTArrayFrom(a1)
t.Assert(len(array1.Join(".")), 0)
})
}
func TestTArray_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
array1 := garray.NewTArrayFrom(a1)
t.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
array1 = nil
t.Assert(array1.String(), "")
})
}
func TestTArray_Replace(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
a2 := []any{"a", "b", "c"}
a3 := []any{"m", "n", "p", "z", "x", "y", "d", "u"}
array1 := garray.NewTArrayFrom(a1)
array2 := array1.Replace(a2)
t.Assert(array2.Len(), 7)
t.Assert(array2.Contains("b"), true)
t.Assert(array2.Contains(4), true)
t.Assert(array2.Contains("v"), false)
array3 := array1.Replace(a3)
t.Assert(array3.Len(), 7)
t.Assert(array3.Contains(4), false)
t.Assert(array3.Contains("p"), true)
t.Assert(array3.Contains("u"), false)
})
}
func TestTArray_SetArray(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3, 4, 5, 6}
a2 := []any{"a", "b", "c"}
array1 := garray.NewTArrayFrom(a1)
array1 = array1.SetArray(a2)
t.Assert(array1.Len(), 3)
t.Assert(array1.Contains("b"), true)
t.Assert(array1.Contains("5"), false)
})
}
func TestTArray_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3}
a2 := []any{"a", "b", "c"}
a3 := []any{"a", "1", "2"}
array1 := garray.NewTArrayFrom(a1)
array2 := garray.NewTArrayFrom(a2)
array3 := garray.NewTArrayFrom(a3)
t.Assert(array1.Sum(), 6)
t.Assert(array2.Sum(), 0)
t.Assert(array3.Sum(), 3)
})
}
func TestTArray_Clone(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{0, 1, 2, 3}
array1 := garray.NewTArrayFrom(a1)
array2 := array1.Clone()
t.Assert(array1.Len(), 4)
t.Assert(array2.Sum(), 6)
t.AssertEQ(array1, array2)
})
}
func TestTArray_CountValues(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []any{"a", "b", "c", "d", "e", "d"}
array1 := garray.NewTArrayFrom(a1)
array2 := array1.CountValues()
t.Assert(len(array2), 5)
t.Assert(array2["b"], 1)
t.Assert(array2["d"], 2)
})
}
func TestTArray_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewTArrayFrom(s1, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 3)
// go1
go a1.LockFunc(func(n1 []any) { // 读写锁
time.Sleep(2 * time.Second) // 暂停2秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
})
// go2
go func() {
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后再开始执行.
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
a1.Len()
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
}()
t1 := <-ch1
t2 := <-ch1
<-ch2 // 等待go1完成
// 防止ci抖动,以豪秒为单位
t.AssertGT(t2-t1, 20) // go1加的读写互斥锁所go2读的时候被阻塞。
t.Assert(a1.Contains("g"), true)
})
}
func TestTArray_RLockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "c", "d"}
a1 := garray.NewTArrayFrom(s1, true)
ch1 := make(chan int64, 3)
ch2 := make(chan int64, 1)
// go1
go a1.RLockFunc(func(n1 []any) { // 读锁
time.Sleep(2 * time.Second) // 暂停1秒
n1[2] = "g"
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
})
// go2
go func() {
time.Sleep(100 * time.Millisecond) // 故意暂停0.01秒,等go1执行锁后再开始执行.
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
a1.Len()
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
}()
t1 := <-ch1
t2 := <-ch1
<-ch2 // 等待go1完成
// 防止ci抖动,以豪秒为单位
t.AssertLT(t2-t1, 20) // go1加的读锁所go2读的时候并没有阻塞。
t.Assert(a1.Contains("g"), false)
})
}
func TestTArray_Json(t *testing.T) {
// pointer
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "d", "c"}
a1 := garray.NewTArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.New()
err2 = json.UnmarshalUseNumber(b2, &a2)
t.Assert(err2, nil)
t.Assert(a2.Slice(), s1)
var a3 garray.Array
err := json.UnmarshalUseNumber(b2, &a3)
t.AssertNil(err)
t.Assert(a3.Slice(), s1)
})
// value.
gtest.C(t, func(t *gtest.T) {
s1 := []any{"a", "b", "d", "c"}
a1 := *garray.NewTArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.New()
err2 = json.UnmarshalUseNumber(b2, &a2)
t.Assert(err2, nil)
t.Assert(a2.Slice(), s1)
var a3 garray.Array
err := json.UnmarshalUseNumber(b2, &a3)
t.AssertNil(err)
t.Assert(a3.Slice(), s1)
})
// pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores *garray.Array
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.AssertNil(err)
user := new(User)
err = json.UnmarshalUseNumber(b, user)
t.AssertNil(err)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
// value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.Array
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.AssertNil(err)
user := new(User)
err = json.UnmarshalUseNumber(b, user)
t.AssertNil(err)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
}
func TestTArray_Iterator(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewTArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
array.Iterator(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorAsc(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
array.IteratorDesc(func(k int, v any) bool {
t.Assert(v, slice[k])
return true
})
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.Iterator(func(k int, v any) bool {
index++
return false
})
t.Assert(index, 1)
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorAsc(func(k int, v any) bool {
index++
return false
})
t.Assert(index, 1)
})
gtest.C(t, func(t *gtest.T) {
index := 0
array.IteratorDesc(func(k int, v any) bool {
index++
return false
})
t.Assert(index, 1)
})
}
func TestTArray_RemoveValue(t *testing.T) {
slice := g.Slice{"a", "b", "d", "c"}
array := garray.NewTArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
t.Assert(array.RemoveValue("e"), false)
t.Assert(array.RemoveValue("b"), true)
t.Assert(array.RemoveValue("a"), true)
t.Assert(array.RemoveValue("c"), true)
t.Assert(array.RemoveValue("f"), false)
})
}
func TestTArray_RemoveValues(t *testing.T) {
slice := g.SliceStr{"a", "b", "d", "c"}
array := garray.NewTArrayFrom(slice)
gtest.C(t, func(t *gtest.T) {
array.RemoveValues("a", "b", "c")
t.Assert(array.Slice(), g.Slice{"d"})
})
}
func TestTArray_UnmarshalValue(t *testing.T) {
type V struct {
Name string
Array *garray.Array
}
// JSON
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": []byte(`[1,2,3]`),
}, &v)
t.AssertNil(err)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
// Map
gtest.C(t, func(t *gtest.T) {
var v *V
err := gconv.Struct(g.Map{
"name": "john",
"array": g.Slice{1, 2, 3},
}, &v)
t.AssertNil(err)
t.Assert(v.Name, "john")
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
})
}
func TestTArray_FilterNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewTArrayFromCopy(values)
t.Assert(array.FilterNil().Slice(), values)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
})
}
func TestTArray_Filter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
array := garray.NewTArrayFromCopy(values)
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}).Slice(), values)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsNil(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{1, 2, 3, 4})
t.Assert(array.Filter(func(index int, value any) bool {
return empty.IsEmpty(value)
}), g.Slice{1, 2, 3, 4})
})
}
func TestTArray_FilterEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{1, 2, 3, 4})
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
}
func TestTArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewTArrayFrom(g.Slice{"1", "2"})
t.Assert(array.Walk(func(value any) any {
return "key-" + gconv.String(value)
}), g.Slice{"key-1", "key-2"})
})
}