Skip to content

Commit

Permalink
👔 up: arrutil - upgrade some util func to generic version, add more t…
Browse files Browse the repository at this point in the history
…ests
  • Loading branch information
inhere committed Jun 29, 2023
1 parent ff6a0db commit e083d92
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 80 deletions.
53 changes: 6 additions & 47 deletions arrutil/arrutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,11 @@ import (
"github.com/gookit/goutil/mathutil"
)

// Reverse string slice [site user info 0] -> [0 info user site]
func Reverse(ss []string) {
ln := len(ss)
for i := 0; i < ln/2; i++ {
li := ln - i - 1
ss[i], ss[li] = ss[li], ss[i]
}
}

// StringsRemove a value form a string slice
// StringsRemove value form a string slice
func StringsRemove(ss []string, s string) []string {
ns := make([]string, 0, len(ss))
for _, v := range ss {
if v != s {
ns = append(ns, v)
}
}
return ns
return StringsFilter(ss, func(el string) bool {
return s != el
})
}

// StringsFilter given strings, default will filter emtpy string.
Expand All @@ -34,8 +21,8 @@ func StringsRemove(ss []string, s string) []string {
//
// // output: [a, b]
// ss := arrutil.StringsFilter([]string{"a", "", "b", ""})
func StringsFilter(ss []string, filter ...func(s string) bool) []string {
var fn func(s string) bool
func StringsFilter(ss []string, filter ...comdef.StringMatchFunc) []string {
var fn comdef.StringMatchFunc
if len(filter) > 0 && filter[0] != nil {
fn = filter[0]
} else {
Expand Down Expand Up @@ -102,31 +89,3 @@ func RandomOne[T any](arr []T) T {
}
panic("cannot get value from nil or empty slice")
}

// Unique value in the given slice data.
func Unique[T ~string | comdef.XintOrFloat](list []T) []T {
if len(list) < 2 {
return list
}

valMap := make(map[T]struct{}, len(list))
uniArr := make([]T, 0, len(list))

for _, t := range list {
if _, ok := valMap[t]; !ok {
valMap[t] = struct{}{}
uniArr = append(uniArr, t)
}
}
return uniArr
}

// IndexOf value in given slice.
func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int {
for i, v := range list {
if v == val {
return i
}
}
return -1
}
17 changes: 10 additions & 7 deletions arrutil/arrutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ import (
"github.com/gookit/goutil/testutil/assert"
)

func TestReverse(t *testing.T) {
ss := []string{"a", "b", "c"}

arrutil.Reverse(ss)
assert.Eq(t, []string{"c", "b", "a"}, ss)
}

func TestStringsRemove(t *testing.T) {
ss := []string{"a", "b", "c"}
ns := arrutil.StringsRemove(ss, "b")
Expand All @@ -31,6 +24,15 @@ func TestStringsFilter(t *testing.T) {
is.Eq([]string{"a", "b"}, ss)
}

func TestStringsMap(t *testing.T) {
is := assert.New(t)

ss := arrutil.StringsMap([]string{"a", "b", "c"}, func(s string) string {
return s + "1"
})
is.Eq([]string{"a1", "b1", "c1"}, ss)
}

func TestTrimStrings(t *testing.T) {
is := assert.New(t)

Expand Down Expand Up @@ -120,6 +122,7 @@ func TestGetRandomOne(t *testing.T) {
}

func TestUnique(t *testing.T) {
assert.Eq(t, []int{2}, arrutil.Unique[int]([]int{2}))
assert.Eq(t, []int{2, 3, 4}, arrutil.Unique[int]([]int{2, 3, 2, 4}))
assert.Eq(t, []uint{2, 3, 4}, arrutil.Unique([]uint{2, 3, 2, 4}))
assert.Eq(t, []string{"ab", "bc", "cd"}, arrutil.Unique([]string{"ab", "bc", "ab", "cd"}))
Expand Down
4 changes: 2 additions & 2 deletions arrutil/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ var (
// item: the element to search.
// fn: the comparer function.
// return: the index of the element, or -1 if not found.
func TwowaySearch(data any, item any, fn Comparer) (int, error) {
func TwowaySearch(data, item any, fn Comparer) (int, error) {
if data == nil {
return -1, errors.New("collections.TwowaySearch: data is nil")
}
Expand Down Expand Up @@ -255,7 +255,7 @@ func Excepts(first, second any, fn Comparer) any {
// second: the second slice. MUST BE A SLICE.
// fn: the comparer function.
// returns: to intersect of the two slices.
func Intersects(first any, second any, fn Comparer) any {
func Intersects(first, second any, fn Comparer) any {
typeOfFirst := reflect.TypeOf(first)
if typeOfFirst.Kind() != reflect.Slice {
panic("collections.Intersects: first must be a slice")
Expand Down
22 changes: 0 additions & 22 deletions arrutil/collection_gte118.go

This file was deleted.

2 changes: 1 addition & 1 deletion arrutil/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func AnyToString(arr any) string {
func SliceToString(arr ...any) string { return ToString(arr) }

// ToString simple and quickly convert []any to string
func ToString(arr []any) string {
func ToString[T any](arr []T) string {
// like fmt.Println([]any(nil))
if arr == nil {
return "[]"
Expand Down
2 changes: 2 additions & 0 deletions arrutil/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ func TestSliceToString(t *testing.T) {
is := assert.New(t)

is.Eq("[]", arrutil.SliceToString(nil))
is.Eq("[]", arrutil.ToString[any](nil))
is.Eq("[a,b]", arrutil.ToString([]string{"a", "b"}))
is.Eq("[a,b]", arrutil.SliceToString("a", "b"))
}

Expand Down
29 changes: 28 additions & 1 deletion arrutil/enum_test.go → arrutil/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/gookit/goutil/testutil/assert"
)

func TestInts_Has_String(t *testing.T) {
func TestInts_methods(t *testing.T) {
tests := []struct {
is arrutil.Ints
val int
Expand All @@ -27,6 +27,12 @@ func TestInts_Has_String(t *testing.T) {
assert.False(t, tt.is.Has(999))
assert.Eq(t, tt.want2, tt.is.String())
}

ints := arrutil.Ints{23, 10, 12}
ints.Sort()
assert.Eq(t, "10,12,23", ints.String())
assert.Eq(t, 10, ints.First())
assert.Eq(t, 23, ints.Last())
}

func TestStrings_methods(t *testing.T) {
Expand All @@ -52,4 +58,25 @@ func TestStrings_methods(t *testing.T) {

ss := arrutil.Strings{"a", "b"}
assert.Eq(t, "a b", ss.Join(" "))
assert.Eq(t, "a", ss.First())
}

func TestScalarList_methods(t *testing.T) {
ls := arrutil.ScalarList[string]{"a", "", "b"}
assert.Eq(t, "a", ls.First())
assert.Eq(t, "b", ls.Last())
assert.True(t, ls.Has("a"))
assert.False(t, ls.IsEmpty())
assert.Eq(t, "[a,b]", ls.Filter().String())
assert.Eq(t, "[a,b]", ls.Remove("").String())

t.Run("panic", func(t *testing.T) {
ls = arrutil.ScalarList[string]{}
assert.Panics(t, func() {
ls.First()
})
assert.Panics(t, func() {
ls.Last()
})
})
}
102 changes: 102 additions & 0 deletions arrutil/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package arrutil

import (
"reflect"

"github.com/gookit/goutil/comdef"
)

// Reverse any T slice.
//
// eg: []string{"site", "user", "info", "0"} -> []string{"0", "info", "user", "site"}
func Reverse[T any](ls []T) {
ln := len(ls)
for i := 0; i < ln/2; i++ {
li := ln - i - 1
ls[i], ls[li] = ls[li], ls[i]
}
}

// Remove give element from slice []T.
//
// eg: []string{"site", "user", "info", "0"} -> []string{"site", "user", "info"}
func Remove[T comdef.Compared](ls []T, val T) []T {
return Filter(ls, func(el T) bool {
return el != val
})
}

// Filter given slice, default will filter zero value.
//
// Usage:
//
// // output: [a, b]
// ss := arrutil.Filter([]string{"a", "", "b", ""})
func Filter[T any](ls []T, filter ...comdef.MatchFunc[T]) []T {
var fn comdef.MatchFunc[T]
if len(filter) > 0 && filter[0] != nil {
fn = filter[0]
} else {
fn = func(el T) bool {
return !reflect.ValueOf(el).IsZero()
}
}

newLs := make([]T, 0, len(ls))
for _, el := range ls {
if fn(el) {
newLs = append(newLs, el)
}
}
return newLs
}

// MapFn map handle function type.
type MapFn[T any, V any] func(input T) (target V, find bool)

// Map a list to new list
//
// eg: mapping [object0{},object1{},...] to flatten list [object0.someKey, object1.someKey, ...]
func Map[T any, V any](list []T, mapFn MapFn[T, V]) []V {
flatArr := make([]V, 0, len(list))

for _, obj := range list {
if target, ok := mapFn(obj); ok {
flatArr = append(flatArr, target)
}
}
return flatArr
}

// Column alias of Map func
func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V {
return Map(list, mapFn)
}

// Unique value in the given slice data.
func Unique[T ~string | comdef.XintOrFloat](list []T) []T {
if len(list) < 2 {
return list
}

valMap := make(map[T]struct{}, len(list))
uniArr := make([]T, 0, len(list))

for _, t := range list {
if _, ok := valMap[t]; !ok {
valMap[t] = struct{}{}
uniArr = append(uniArr, t)
}
}
return uniArr
}

// IndexOf value in given slice.
func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int {
for i, v := range list {
if v == val {
return i
}
}
return -1
}
34 changes: 34 additions & 0 deletions arrutil/process_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package arrutil_test

import (
"testing"

"github.com/gookit/goutil/arrutil"
"github.com/gookit/goutil/testutil/assert"
)

func TestReverse(t *testing.T) {
ss := []string{"a", "b", "c"}
arrutil.Reverse(ss)
assert.Eq(t, []string{"c", "b", "a"}, ss)

ints := []int{1, 2, 3}
arrutil.Reverse(ints)
assert.Eq(t, []int{3, 2, 1}, ints)
}

func TestRemove(t *testing.T) {
ss := []string{"a", "b", "c"}
ns := arrutil.Remove(ss, "b")
assert.Eq(t, []string{"a", "c"}, ns)

ints := []int{1, 2, 3}
ni := arrutil.Remove(ints, 2)
assert.Eq(t, []int{1, 3}, ni)
}

func TestFilter(t *testing.T) {
is := assert.New(t)
ss := arrutil.Filter([]string{"a", "", "b", ""})
is.Eq([]string{"a", "b"}, ss)
}

0 comments on commit e083d92

Please sign in to comment.