Initial QSfera import
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
# Array/Slice Utils
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
go get github.com/gookit/goutil/arrutil
|
||||
```
|
||||
|
||||
## Go docs
|
||||
|
||||
- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/arrutil)
|
||||
|
||||
## Functions API
|
||||
|
||||
> **Note**: doc by run `go doc ./arrutil`
|
||||
|
||||
```go
|
||||
func AnyToString(arr any) string
|
||||
func CloneSlice(data any) interface{}
|
||||
func Contains(arr, val any) bool
|
||||
func ExceptWhile(data any, fn Predicate) interface{}
|
||||
func Excepts(first, second any, fn Comparer) interface{}
|
||||
func Find(source any, fn Predicate) (interface{}, error)
|
||||
func FindOrDefault(source any, fn Predicate, defaultValue any) interface{}
|
||||
func FormatIndent(arr any, indent string) string
|
||||
func GetRandomOne(arr any) interface{}
|
||||
func HasValue(arr, val any) bool
|
||||
func InStrings(elem string, ss []string) bool
|
||||
func Int64sHas(ints []int64, val int64) bool
|
||||
func Intersects(first any, second any, fn Comparer) interface{}
|
||||
func IntsHas(ints []int, val int) bool
|
||||
func JoinSlice(sep string, arr ...any) string
|
||||
func JoinStrings(sep string, ss ...string) string
|
||||
func MakeEmptySlice(itemType reflect.Type) interface{}
|
||||
func Map[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V
|
||||
func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V
|
||||
func MustToInt64s(arr any) []int64
|
||||
func MustToStrings(arr any) []string
|
||||
func NotContains(arr, val any) bool
|
||||
func RandomOne(arr any) interface{}
|
||||
func Reverse(ss []string)
|
||||
func SliceToInt64s(arr []any) []int64
|
||||
func SliceToString(arr ...any) string
|
||||
func SliceToStrings(arr []any) []string
|
||||
func StringsFilter(ss []string, filter ...func(s string) bool) []string
|
||||
func StringsHas(ss []string, val string) bool
|
||||
func StringsJoin(sep string, ss ...string) string
|
||||
func StringsMap(ss []string, mapFn func(s string) string) []string
|
||||
func StringsRemove(ss []string, s string) []string
|
||||
func StringsToInts(ss []string) (ints []int, err error)
|
||||
func StringsToSlice(ss []string) []interface{}
|
||||
func TakeWhile(data any, fn Predicate) interface{}
|
||||
func ToInt64s(arr any) (ret []int64, err error)
|
||||
func ToString(arr []any) string
|
||||
func ToStrings(arr any) (ret []string, err error)
|
||||
func TrimStrings(ss []string, cutSet ...string) []string
|
||||
func TwowaySearch(data any, item any, fn Comparer) (int, error)
|
||||
func Union(first, second any, fn Comparer) interface{}
|
||||
func Unique(arr any) interface{}
|
||||
type ArrFormatter struct{ ... }
|
||||
func NewFormatter(arr any) *ArrFormatter
|
||||
```
|
||||
|
||||
## Code Check & Testing
|
||||
|
||||
```bash
|
||||
gofmt -w -l ./
|
||||
golint ./...
|
||||
```
|
||||
|
||||
**Testing**:
|
||||
|
||||
```shell
|
||||
go test -v ./cliutil/...
|
||||
```
|
||||
|
||||
**Test limit by regexp**:
|
||||
|
||||
```shell
|
||||
go test -v -run ^TestSetByKeys ./cliutil/...
|
||||
```
|
||||
|
||||
## Refers
|
||||
|
||||
- https://github.com/elliotchance/pie
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
## ArrUtil
|
||||
|
||||
`arrutil` 是一个用于操作数组和切片的工具包,提供了丰富的功能来简化 Go 语言中数组和切片的处理。
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
go get github.com/gookit/goutil/arrutil
|
||||
```
|
||||
|
||||
## Go docs
|
||||
|
||||
- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/arrutil)
|
||||
|
||||
## 基本功能
|
||||
|
||||
主要包括以下功能:
|
||||
|
||||
1. **数组/切片的基本操作**:
|
||||
- `RandomOne`:从数组或切片中随机获取一个元素。
|
||||
- `Reverse`:反转数组或切片中的元素顺序。
|
||||
|
||||
2. **检查和查找**:
|
||||
- `Contains` 和 `HasValue`:检查数组或切片是否包含特定值。
|
||||
- `InStrings` 和 `StringsHas`:检查字符串切片中是否包含特定字符串。
|
||||
- `IntsHas` 和 `Int64sHas`:检查整数切片中是否包含特定整数值。
|
||||
- `Find` 和 `FindOrDefault`:根据谓词函数查找元素,如果没有找到则返回默认值。
|
||||
|
||||
3. **集合操作**:
|
||||
- `Union`:计算两个切片的并集。
|
||||
- `Intersects`:计算两个切片的交集。
|
||||
- `Excepts` 和 `Differences`:计算两个切片的差集。
|
||||
- `TwowaySearch`:在切片中双向搜索特定元素。
|
||||
|
||||
4. **转换和格式化**:
|
||||
- `ToInt64s` 和 `ToStrings`:将任意类型的切片转换为整数或字符串切片。
|
||||
- `JoinSlice` 和 `JoinStrings`:将切片中的元素连接成一个字符串。
|
||||
- `FormatIndent`:将数组或切片格式化为带有缩进的字符串。
|
||||
|
||||
5. **排序和过滤**:
|
||||
- `Sort`:对切片进行排序。
|
||||
- `Filter`:根据条件过滤切片中的元素。
|
||||
- `Remove`:从切片中移除特定元素。
|
||||
|
||||
6. **其他实用功能**:
|
||||
- `Unique`:去除切片中的重复元素。
|
||||
- `FirstOr`:获取切片的第一个元素,如果切片为空则返回默认值。
|
||||
|
||||
这些功能使得在 Go 语言中处理数组和切片变得更加方便和高效。无论是进行数据处理、集合运算还是字符串操作,`arrutil` 都提供了一系列简洁且易于使用的函数来帮助开发者完成任务。
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Package arrutil provides some util functions for array, slice
|
||||
package arrutil
|
||||
|
||||
import (
|
||||
"github.com/gookit/goutil/mathutil"
|
||||
)
|
||||
|
||||
// GetRandomOne get random element from an array/slice
|
||||
func GetRandomOne[T any](arr []T) T { return RandomOne(arr) }
|
||||
|
||||
// RandomOne get random element from an array/slice
|
||||
func RandomOne[T any](arr []T) T {
|
||||
if ln := len(arr); ln > 0 {
|
||||
i := mathutil.RandomInt(0, len(arr))
|
||||
return arr[i]
|
||||
}
|
||||
panic("cannot get value from nil or empty slice")
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
package arrutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/comdef"
|
||||
"github.com/gookit/goutil/mathutil"
|
||||
)
|
||||
|
||||
// SliceHas check the slice contains the given value
|
||||
func SliceHas[T comdef.ScalarType](slice []T, val T) bool {
|
||||
for _, ele := range slice {
|
||||
if ele == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IntsHas check the []comdef.Integer contains the given value
|
||||
func IntsHas[T comdef.Integer](ints []T, val T) bool {
|
||||
for _, ele := range ints {
|
||||
if ele == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Int64sHas check the []int64 contains the given value
|
||||
func Int64sHas(ints []int64, val int64) bool {
|
||||
for _, ele := range ints {
|
||||
if ele == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// StringsHas check the []string contains the given element
|
||||
func StringsHas[T ~string](ss []T, val T) bool {
|
||||
for _, ele := range ss {
|
||||
if ele == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InStrings check elem in the ss. alias of StringsHas()
|
||||
func InStrings[T ~string](elem T, ss []T) bool {
|
||||
return StringsHas(ss, elem)
|
||||
}
|
||||
|
||||
// NotIn check the given value whether not in the list
|
||||
func NotIn[T comdef.ScalarType](value T, list []T) bool {
|
||||
return !In(value, list)
|
||||
}
|
||||
|
||||
// In check the given value whether in the list
|
||||
func In[T comdef.ScalarType](value T, list []T) bool {
|
||||
for _, elem := range list {
|
||||
if elem == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ContainsAll check given values is sub-list of sample list.
|
||||
func ContainsAll[T comdef.ScalarType](list, values []T) bool {
|
||||
return IsSubList(values, list)
|
||||
}
|
||||
|
||||
// IsSubList check given values is sub-list of sample list.
|
||||
func IsSubList[T comdef.ScalarType](values, list []T) bool {
|
||||
for _, value := range values {
|
||||
if !In(value, list) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsParent check given values is parent-list of samples.
|
||||
func IsParent[T comdef.ScalarType](values, list []T) bool {
|
||||
return IsSubList(list, values)
|
||||
}
|
||||
|
||||
// HasValue check array(strings, intXs, uintXs) should be contained the given value(int(X),string).
|
||||
func HasValue(arr, val any) bool { return Contains(arr, val) }
|
||||
|
||||
// Contains check slice/array(strings, intXs, uintXs) should be contained the given value(int(X),string).
|
||||
//
|
||||
// TIP: Difference the In(), Contains() will try to convert value type,
|
||||
// and Contains() support array type.
|
||||
func Contains(arr, val any) bool {
|
||||
if val == nil || arr == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// if is string value
|
||||
if strVal, ok := val.(string); ok {
|
||||
if ss, ok := arr.([]string); ok {
|
||||
return StringsHas(ss, strVal)
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(arr)
|
||||
if rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array {
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if v, ok := rv.Index(i).Interface().(string); ok && strings.EqualFold(v, strVal) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// as int value
|
||||
intVal, err := mathutil.Int64(val)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if int64s, err := ToInt64s(arr); err == nil {
|
||||
return Int64sHas(int64s, intVal)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NotContains check array(strings, ints, uints) should be not contains the given value.
|
||||
func NotContains(arr, val any) bool {
|
||||
return !Contains(arr, val)
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
package arrutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gookit/goutil/comdef"
|
||||
"github.com/gookit/goutil/reflects"
|
||||
)
|
||||
|
||||
// ErrElementNotFound is the error returned when the element is not found.
|
||||
var ErrElementNotFound = errors.New("element not found")
|
||||
|
||||
// Comparer Function to compare two elements.
|
||||
type Comparer[T any] func(a, b T) int
|
||||
|
||||
// type Comparer func(a, b any) int
|
||||
|
||||
// Predicate Function to predicate a struct/value satisfies a condition.
|
||||
type Predicate[T any] func(v T) bool
|
||||
|
||||
// StringEqualsComparer Comparer for string. It will compare the string by their value.
|
||||
//
|
||||
// returns: 0 if equal, -1 if a != b
|
||||
func StringEqualsComparer(a, b string) int {
|
||||
if a == b {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// ValueEqualsComparer Comparer for comdef.Compared type. It will compare by their value.
|
||||
//
|
||||
// returns: 0 if equal, -1 if a != b
|
||||
func ValueEqualsComparer[T comdef.Compared](a, b T) int {
|
||||
if a == b {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// ReflectEqualsComparer Comparer for struct ptr. It will compare by reflect.Value
|
||||
//
|
||||
// returns: 0 if equal, -1 if a != b
|
||||
func ReflectEqualsComparer[T any](a, b T) int {
|
||||
if reflects.IsEqual(a, b) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// ElemTypeEqualsComparer Comparer for struct/value. It will compare the struct by their element type.
|
||||
//
|
||||
// returns: 0 if same type, -1 if not.
|
||||
func ElemTypeEqualsComparer[T any](a, b T) int {
|
||||
at := reflects.TypeOf(a).SafeElem()
|
||||
bt := reflects.TypeOf(b).SafeElem()
|
||||
|
||||
if at == bt {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// TwowaySearch find specialized element in a slice forward and backward in the same time, should be more quickly.
|
||||
//
|
||||
// - data: the slice to search in. MUST BE A SLICE.
|
||||
// - item: the element to search.
|
||||
// - fn: the comparer function.
|
||||
// - return: the index of the element, or -1 if not found.
|
||||
func TwowaySearch[T any](data []T, item T, fn Comparer[T]) (int, error) {
|
||||
if data == nil {
|
||||
return -1, errors.New("collections.TwowaySearch: data is nil")
|
||||
}
|
||||
if fn == nil {
|
||||
return -1, errors.New("collections.TwowaySearch: fn is nil")
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return -1, errors.New("collections.TwowaySearch: data is empty")
|
||||
}
|
||||
|
||||
forward := 0
|
||||
backward := len(data) - 1
|
||||
|
||||
for forward <= backward {
|
||||
if fn(data[forward], item) == 0 {
|
||||
return forward, nil
|
||||
}
|
||||
|
||||
if fn(data[backward], item) == 0 {
|
||||
return backward, nil
|
||||
}
|
||||
|
||||
forward++
|
||||
backward--
|
||||
}
|
||||
|
||||
return -1, ErrElementNotFound
|
||||
}
|
||||
|
||||
// CloneSlice Clone a slice.
|
||||
//
|
||||
// data: the slice to clone.
|
||||
// returns: the cloned slice.
|
||||
func CloneSlice[T any](data []T) []T {
|
||||
nt := make([]T, 0, len(data))
|
||||
nt = append(nt, data...)
|
||||
return nt
|
||||
}
|
||||
|
||||
// Diff Produces the set difference of two slice according to a comparer function. alias of Differences
|
||||
func Diff[T any](first, second []T, fn Comparer[T]) []T {
|
||||
return Differences(first, second, fn)
|
||||
}
|
||||
|
||||
// Differences Produces the set difference of two slice according to a comparer function.
|
||||
//
|
||||
// - first: the first slice. MUST BE A SLICE.
|
||||
// - second: the second slice. MUST BE A SLICE.
|
||||
// - fn: the comparer function.
|
||||
// - returns: the difference of the two slices.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Output: []string{"c"}
|
||||
// Differences([]string{"a", "b", "c"}, []string{"a", "b"}, arrutil.StringEqualsComparer
|
||||
func Differences[T any](first, second []T, fn Comparer[T]) []T {
|
||||
firstLen := len(first)
|
||||
if firstLen == 0 {
|
||||
return CloneSlice(second)
|
||||
}
|
||||
|
||||
secondLen := len(second)
|
||||
if secondLen == 0 {
|
||||
return CloneSlice(first)
|
||||
}
|
||||
|
||||
maxLn := firstLen
|
||||
if secondLen > firstLen {
|
||||
maxLn = secondLen
|
||||
}
|
||||
|
||||
result := make([]T, 0)
|
||||
for i := 0; i < maxLn; i++ {
|
||||
if i < firstLen {
|
||||
s := first[i]
|
||||
if i, _ := TwowaySearch(second, s, fn); i < 0 {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
|
||||
if i < secondLen {
|
||||
t := second[i]
|
||||
if i, _ := TwowaySearch(first, t, fn); i < 0 {
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Excepts Produces the set difference of two slice according to a comparer function.
|
||||
//
|
||||
// - first: the first slice. MUST BE A SLICE.
|
||||
// - second: the second slice. MUST BE A SLICE.
|
||||
// - fn: the comparer function.
|
||||
// - returns: the difference of the two slices.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Output: []string{"c"}
|
||||
// Excepts([]string{"a", "b", "c"}, []string{"a", "b"}, arrutil.StringEqualsComparer)
|
||||
func Excepts[T any](first, second []T, fn Comparer[T]) []T {
|
||||
if len(first) == 0 {
|
||||
return make([]T, 0)
|
||||
}
|
||||
if len(second) == 0 {
|
||||
return CloneSlice(first)
|
||||
}
|
||||
|
||||
result := make([]T, 0)
|
||||
for _, s := range first {
|
||||
if i, _ := TwowaySearch(second, s, fn); i < 0 {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Intersects Produces to intersect of two slice according to a comparer function.
|
||||
//
|
||||
// - first: the first slice. MUST BE A SLICE.
|
||||
// - second: the second slice. MUST BE A SLICE.
|
||||
// - fn: the comparer function.
|
||||
// - returns: to intersect of the two slices.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Output: []string{"a", "b"}
|
||||
// Intersects([]string{"a", "b", "c"}, []string{"a", "b"}, arrutil.ValueEqualsComparer)
|
||||
func Intersects[T any](first, second []T, fn Comparer[T]) []T {
|
||||
if len(first) == 0 || len(second) == 0 {
|
||||
return make([]T, 0)
|
||||
}
|
||||
|
||||
result := make([]T, 0)
|
||||
for _, s := range first {
|
||||
if i, _ := TwowaySearch(second, s, fn); i >= 0 {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Union Produces the set union of two slice according to a comparer function
|
||||
//
|
||||
// - first: the first slice. MUST BE A SLICE.
|
||||
// - second: the second slice. MUST BE A SLICE.
|
||||
// - fn: the comparer function.
|
||||
// - returns: the union of the two slices.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Output: []string{"a", "b", "c"}
|
||||
// sl := Union([]string{"a", "b", "c"}, []string{"a", "b"}, arrutil.ValueEqualsComparer)
|
||||
func Union[T any](first, second []T, fn Comparer[T]) []T {
|
||||
if len(first) == 0 {
|
||||
return CloneSlice(second)
|
||||
}
|
||||
|
||||
excepts := Excepts(second, first, fn)
|
||||
nt := make([]T, 0, len(first)+len(second))
|
||||
nt = append(nt, first...)
|
||||
return append(nt, excepts...)
|
||||
}
|
||||
|
||||
// Find Produces the value of a slice according to a predicate function.
|
||||
//
|
||||
// - source: the slice. MUST BE A SLICE.
|
||||
// - fn: the predicate function.
|
||||
// - returns: the struct/value of the slice.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Output: "c"
|
||||
// val := Find([]string{"a", "b", "c"}, func(s string) bool {
|
||||
// return s == "c"
|
||||
// })
|
||||
func Find[T any](source []T, fn Predicate[T]) (v T, err error) {
|
||||
err = ErrElementNotFound
|
||||
if len(source) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, s := range source {
|
||||
if fn(s) {
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindOrDefault Produce the value f a slice to a predicate function,
|
||||
// Produce default value when predicate function not found.
|
||||
//
|
||||
// - source: the slice. MUST BE A SLICE.
|
||||
// - fn: the predicate function.
|
||||
// - defaultValue: the default value.
|
||||
// - returns: the value of the slice.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Output: "d"
|
||||
// val := FindOrDefault([]string{"a", "b", "c"}, func(s string) bool {
|
||||
// return s == "d"
|
||||
// }, "d")
|
||||
func FindOrDefault[T any](source []T, fn Predicate[T], defaultValue T) T {
|
||||
item, err := Find(source, fn)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
// TakeWhile Produce the set of a slice according to a predicate function,
|
||||
// Produce empty slice when predicate function not matched.
|
||||
//
|
||||
// - data: the slice. MUST BE A SLICE.
|
||||
// - fn: the predicate function.
|
||||
// - returns: the set of the slice.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Output: []string{"a", "b"}
|
||||
// sl := TakeWhile([]string{"a", "b", "c"}, func(s string) bool {
|
||||
// return s != "c"
|
||||
// })
|
||||
func TakeWhile[T any](data []T, fn Predicate[T]) []T {
|
||||
result := make([]T, 0)
|
||||
if len(data) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
if fn(v) {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ExceptWhile Produce the set of a slice except with a predicate function,
|
||||
// Produce original slice when predicate function not match.
|
||||
//
|
||||
// - data: the slice. MUST BE A SLICE.
|
||||
// - fn: the predicate function.
|
||||
// - returns: the set of the slice.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Output: []string{"a", "b"}
|
||||
// sl := ExceptWhile([]string{"a", "b", "c"}, func(s string) bool {
|
||||
// return s == "c"
|
||||
// })
|
||||
func ExceptWhile[T any](data []T, fn Predicate[T]) []T {
|
||||
result := make([]T, 0)
|
||||
if len(data) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
if !fn(v) {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
package arrutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/comdef"
|
||||
"github.com/gookit/goutil/mathutil"
|
||||
"github.com/gookit/goutil/reflects"
|
||||
"github.com/gookit/goutil/strutil"
|
||||
)
|
||||
|
||||
// ErrInvalidType error
|
||||
var ErrInvalidType = errors.New("the input param type is invalid")
|
||||
|
||||
/*************************************************************
|
||||
* Join func for slice
|
||||
*************************************************************/
|
||||
|
||||
// JoinStrings alias of strings.Join
|
||||
func JoinStrings(sep string, ss ...string) string {
|
||||
return strings.Join(ss, sep)
|
||||
}
|
||||
|
||||
// StringsJoin alias of strings.Join
|
||||
func StringsJoin(sep string, ss ...string) string {
|
||||
return strings.Join(ss, sep)
|
||||
}
|
||||
|
||||
// JoinTyped join typed []T slice to string.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// JoinTyped(",", 1,2,3) // "1,2,3"
|
||||
// JoinTyped(",", "a","b","c") // "a,b,c"
|
||||
// JoinTyped[any](",", "a",1,"c") // "a,1,c"
|
||||
func JoinTyped[T any](sep string, arr ...T) string {
|
||||
if arr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
for i, v := range arr {
|
||||
if i > 0 {
|
||||
sb.WriteString(sep)
|
||||
}
|
||||
sb.WriteString(strutil.QuietString(v))
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// JoinSlice join []any slice to string.
|
||||
func JoinSlice(sep string, arr ...any) string {
|
||||
if arr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
for i, v := range arr {
|
||||
if i > 0 {
|
||||
sb.WriteString(sep)
|
||||
}
|
||||
sb.WriteString(strutil.QuietString(v))
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* convert func for ints
|
||||
*************************************************************/
|
||||
|
||||
// IntsToString convert []T to string
|
||||
func IntsToString[T comdef.Integer](ints []T) string {
|
||||
if len(ints) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteByte('[')
|
||||
for i, v := range ints {
|
||||
if i > 0 {
|
||||
sb.WriteByte(',')
|
||||
}
|
||||
sb.WriteString(strconv.FormatInt(int64(v), 10))
|
||||
}
|
||||
sb.WriteByte(']')
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// ToInt64s convert any(allow: array,slice) to []int64
|
||||
func ToInt64s(arr any) (ret []int64, err error) {
|
||||
rv := reflect.ValueOf(arr)
|
||||
if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array {
|
||||
err = ErrInvalidType
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
i64, err := mathutil.Int64(rv.Index(i).Interface())
|
||||
if err != nil {
|
||||
return []int64{}, err
|
||||
}
|
||||
|
||||
ret = append(ret, i64)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MustToInt64s convert any(allow: array,slice) to []int64
|
||||
func MustToInt64s(arr any) []int64 {
|
||||
ret, _ := ToInt64s(arr)
|
||||
return ret
|
||||
}
|
||||
|
||||
// SliceToInt64s convert []any to []int64
|
||||
func SliceToInt64s(arr []any) []int64 {
|
||||
i64s := make([]int64, len(arr))
|
||||
for i, v := range arr {
|
||||
i64s[i] = mathutil.QuietInt64(v)
|
||||
}
|
||||
return i64s
|
||||
}
|
||||
|
||||
// ToMap convert a list to new map.
|
||||
//
|
||||
// Example:
|
||||
// type User struct {
|
||||
// Name string
|
||||
// Age int
|
||||
// }
|
||||
// users := []User{{"Tom", 18}, {"Jack", 20}}
|
||||
// mp := arrutil.ToMap(users, func(u User) (string, int) {
|
||||
// return u.Name, u.Age
|
||||
// })
|
||||
// // mp = map[string]int{"Tom":18, "Jack":20}
|
||||
func ToMap[T any, K comdef.ScalarType, V any](list []T, mapFn func(T) (K, V)) map[K]V {
|
||||
mp := make(map[K]V, len(list))
|
||||
for _, item := range list {
|
||||
k, v := mapFn(item)
|
||||
mp[k] = v
|
||||
}
|
||||
return mp
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* convert func for any-slice
|
||||
*************************************************************/
|
||||
|
||||
// AnyToSlice convert any(allow: array,slice) to []any
|
||||
func AnyToSlice(sl any) (ls []any, err error) {
|
||||
rfKeys := reflect.ValueOf(sl)
|
||||
if rfKeys.Kind() != reflect.Slice && rfKeys.Kind() != reflect.Array {
|
||||
return nil, ErrInvalidType
|
||||
}
|
||||
|
||||
for i := 0; i < rfKeys.Len(); i++ {
|
||||
ls = append(ls, rfKeys.Index(i).Interface())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AnyToStrings convert array or slice to []string
|
||||
func AnyToStrings(arr any) []string {
|
||||
ret, _ := ToStrings(arr)
|
||||
return ret
|
||||
}
|
||||
|
||||
// MustToStrings convert array or slice to []string
|
||||
func MustToStrings(arr any) []string {
|
||||
ret, err := ToStrings(arr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// ToStrings convert any(allow: array,slice) to []string
|
||||
func ToStrings(arr any) (ret []string, err error) {
|
||||
// try direct convert
|
||||
switch typVal := arr.(type) {
|
||||
case string:
|
||||
return []string{typVal}, nil
|
||||
case []string:
|
||||
return typVal, nil
|
||||
case []any:
|
||||
return SliceToStrings(typVal), nil
|
||||
}
|
||||
|
||||
// try use reflect to convert
|
||||
rv := reflect.ValueOf(arr)
|
||||
if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array {
|
||||
err = ErrInvalidType
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
str, err1 := strutil.ToString(rv.Index(i).Interface())
|
||||
if err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
ret = append(ret, str)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SliceToStrings safe convert []any to []string
|
||||
func SliceToStrings(arr []any) []string {
|
||||
ss := make([]string, len(arr))
|
||||
for i, v := range arr {
|
||||
ss[i] = strutil.SafeString(v)
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
// QuietStrings safe convert []any to []string
|
||||
func QuietStrings(arr []any) []string { return SliceToStrings(arr) }
|
||||
|
||||
// ConvType convert type of slice elements to new type slice, by the given newElemTyp type.
|
||||
//
|
||||
// Supports conversion between []string, []intX, []uintX, []floatX.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// ints, _ := arrutil.ConvType([]string{"12", "23"}, 1) // []int{12, 23}
|
||||
func ConvType[T any, R any](arr []T, newElemTyp R) ([]R, error) {
|
||||
newArr := make([]R, len(arr))
|
||||
elemTyp := reflect.TypeOf(newElemTyp)
|
||||
|
||||
for i, elem := range arr {
|
||||
var anyElem any = elem
|
||||
// type is same.
|
||||
if _, ok := anyElem.(R); ok {
|
||||
newArr[i] = anyElem.(R)
|
||||
continue
|
||||
}
|
||||
|
||||
// need conv type.
|
||||
rfVal, err := reflects.ValueByType(elem, elemTyp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newArr[i] = rfVal.Interface().(R)
|
||||
}
|
||||
return newArr, nil
|
||||
}
|
||||
|
||||
// AnyToString simple and quickly convert any array, slice to string
|
||||
func AnyToString(arr any) string {
|
||||
return NewFormatter(arr).Format()
|
||||
}
|
||||
|
||||
// SliceToString convert []any to string
|
||||
func SliceToString(arr ...any) string { return ToString(arr) }
|
||||
|
||||
// ToString simple and quickly convert []T to string
|
||||
func ToString[T any](arr []T) string {
|
||||
// like fmt.Println([]any(nil))
|
||||
if arr == nil {
|
||||
return "[]"
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteByte('[')
|
||||
|
||||
for i, v := range arr {
|
||||
if i > 0 {
|
||||
sb.WriteByte(',')
|
||||
}
|
||||
sb.WriteString(strutil.SafeString(v))
|
||||
}
|
||||
|
||||
sb.WriteByte(']')
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// CombineToMap combine []K and []V slice to map[K]V.
|
||||
//
|
||||
// If keys length is greater than values, the extra keys will be ignored.
|
||||
func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V {
|
||||
ln := len(values)
|
||||
mp := make(map[K]V, len(keys))
|
||||
|
||||
for i, key := range keys {
|
||||
if i >= ln {
|
||||
break
|
||||
}
|
||||
mp[key] = values[i]
|
||||
}
|
||||
return mp
|
||||
}
|
||||
|
||||
// CombineToSMap combine two string-slice to map[string]string
|
||||
func CombineToSMap(keys, values []string) map[string]string {
|
||||
ln := len(values)
|
||||
mp := make(map[string]string, len(keys))
|
||||
|
||||
for i, key := range keys {
|
||||
if ln > i {
|
||||
mp[key] = values[i]
|
||||
} else {
|
||||
mp[key] = ""
|
||||
}
|
||||
}
|
||||
return mp
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package arrutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"github.com/gookit/goutil/comdef"
|
||||
"github.com/gookit/goutil/strutil"
|
||||
)
|
||||
|
||||
// ArrFormatter struct
|
||||
type ArrFormatter struct {
|
||||
comdef.BaseFormatter
|
||||
// Prefix string for each element
|
||||
Prefix string
|
||||
// Indent string for format each element
|
||||
Indent string
|
||||
// ClosePrefix on before end char: ]
|
||||
ClosePrefix string
|
||||
}
|
||||
|
||||
// NewFormatter instance
|
||||
func NewFormatter(arr any) *ArrFormatter {
|
||||
f := &ArrFormatter{}
|
||||
f.Src = arr
|
||||
return f
|
||||
}
|
||||
|
||||
// FormatIndent array data to string.
|
||||
func FormatIndent(arr any, indent string) string {
|
||||
return NewFormatter(arr).WithIndent(indent).Format()
|
||||
}
|
||||
|
||||
// WithFn for config self
|
||||
func (f *ArrFormatter) WithFn(fn func(f *ArrFormatter)) *ArrFormatter {
|
||||
fn(f)
|
||||
return f
|
||||
}
|
||||
|
||||
// WithIndent string
|
||||
func (f *ArrFormatter) WithIndent(indent string) *ArrFormatter {
|
||||
f.Indent = indent
|
||||
return f
|
||||
}
|
||||
|
||||
// FormatTo to custom buffer
|
||||
func (f *ArrFormatter) FormatTo(w io.Writer) {
|
||||
f.SetOutput(w)
|
||||
f.doFormat()
|
||||
}
|
||||
|
||||
// Format to string
|
||||
func (f *ArrFormatter) String() string {
|
||||
return f.Format()
|
||||
}
|
||||
|
||||
// Format to string
|
||||
func (f *ArrFormatter) Format() string {
|
||||
f.doFormat()
|
||||
return f.BsWriter().String()
|
||||
}
|
||||
|
||||
// Format to string
|
||||
//
|
||||
//goland:noinspection GoUnhandledErrorResult
|
||||
func (f *ArrFormatter) doFormat() {
|
||||
if f.Src == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rv, ok := f.Src.(reflect.Value)
|
||||
if !ok {
|
||||
rv = reflect.ValueOf(f.Src)
|
||||
}
|
||||
|
||||
rv = reflect.Indirect(rv)
|
||||
if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array {
|
||||
return
|
||||
}
|
||||
|
||||
writer := f.BsWriter()
|
||||
arrLn := rv.Len()
|
||||
if arrLn == 0 {
|
||||
writer.WriteString("[]")
|
||||
return
|
||||
}
|
||||
|
||||
// if f.AfterReset {
|
||||
// defer f.Reset()
|
||||
// }
|
||||
|
||||
// sb.Grow(arrLn * 4)
|
||||
writer.WriteByte('[')
|
||||
|
||||
indentLn := len(f.Indent)
|
||||
if indentLn > 0 {
|
||||
writer.WriteByte('\n')
|
||||
}
|
||||
|
||||
for i := 0; i < arrLn; i++ {
|
||||
if indentLn > 0 {
|
||||
writer.WriteString(f.Indent)
|
||||
}
|
||||
writer.WriteString(strutil.QuietString(rv.Index(i).Interface()))
|
||||
|
||||
if i < arrLn-1 {
|
||||
writer.WriteByte(',')
|
||||
|
||||
// no indent, with space
|
||||
if indentLn == 0 {
|
||||
writer.WriteByte(' ')
|
||||
}
|
||||
}
|
||||
if indentLn > 0 {
|
||||
writer.WriteByte('\n')
|
||||
}
|
||||
}
|
||||
|
||||
if f.ClosePrefix != "" {
|
||||
writer.WriteString(f.ClosePrefix)
|
||||
}
|
||||
writer.WriteByte(']')
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package arrutil
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/comdef"
|
||||
)
|
||||
|
||||
// Ints type
|
||||
type Ints[T comdef.Integer] []T
|
||||
|
||||
// String to string
|
||||
func (is Ints[T]) String() string {
|
||||
return ToString(is)
|
||||
}
|
||||
|
||||
// Has given element
|
||||
func (is Ints[T]) Has(i T) bool {
|
||||
for _, iv := range is {
|
||||
if i == iv {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// First element value.
|
||||
func (is Ints[T]) First(defVal ...T) T {
|
||||
if len(is) > 0 {
|
||||
return is[0]
|
||||
}
|
||||
|
||||
if len(defVal) > 0 {
|
||||
return defVal[0]
|
||||
}
|
||||
panic("empty integer slice")
|
||||
}
|
||||
|
||||
// Last element value.
|
||||
func (is Ints[T]) Last(defVal ...T) T {
|
||||
if len(is) > 0 {
|
||||
return is[len(is)-1]
|
||||
}
|
||||
|
||||
if len(defVal) > 0 {
|
||||
return defVal[0]
|
||||
}
|
||||
panic("empty integer slice")
|
||||
}
|
||||
|
||||
// Sort the int slice
|
||||
func (is Ints[T]) Sort() {
|
||||
sort.Sort(is)
|
||||
}
|
||||
|
||||
// Len get length
|
||||
func (is Ints[T]) Len() int {
|
||||
return len(is)
|
||||
}
|
||||
|
||||
// Less compare two elements
|
||||
func (is Ints[T]) Less(i, j int) bool {
|
||||
return is[i] < is[j]
|
||||
}
|
||||
|
||||
// Swap elements by indexes
|
||||
func (is Ints[T]) Swap(i, j int) {
|
||||
is[i], is[j] = is[j], is[i]
|
||||
}
|
||||
|
||||
// Strings type
|
||||
type Strings []string
|
||||
|
||||
// String to string
|
||||
func (ss Strings) String() string {
|
||||
return strings.Join(ss, ",")
|
||||
}
|
||||
|
||||
// Join to string
|
||||
func (ss Strings) Join(sep string) string {
|
||||
return strings.Join(ss, sep)
|
||||
}
|
||||
|
||||
// Has given element
|
||||
func (ss Strings) Has(sub string) bool {
|
||||
return ss.Contains(sub)
|
||||
}
|
||||
|
||||
// Contains given element
|
||||
func (ss Strings) Contains(sub string) bool {
|
||||
for _, s := range ss {
|
||||
if s == sub {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// First element value.
|
||||
func (ss Strings) First(defVal ...string) string {
|
||||
if len(ss) > 0 {
|
||||
return ss[0]
|
||||
}
|
||||
|
||||
if len(defVal) > 0 {
|
||||
return defVal[0]
|
||||
}
|
||||
panic("empty string list")
|
||||
}
|
||||
|
||||
// Last element value.
|
||||
func (ss Strings) Last(defVal ...string) string {
|
||||
if len(ss) > 0 {
|
||||
return ss[len(ss)-1]
|
||||
}
|
||||
|
||||
if len(defVal) > 0 {
|
||||
return defVal[0]
|
||||
}
|
||||
panic("empty string list")
|
||||
}
|
||||
|
||||
// Sort the string slice
|
||||
func (ss Strings) Sort() {
|
||||
sort.Strings(ss)
|
||||
}
|
||||
|
||||
// SortedList definition for compared type
|
||||
type SortedList[T comdef.Compared] []T
|
||||
|
||||
// Len get length
|
||||
func (ls SortedList[T]) Len() int {
|
||||
return len(ls)
|
||||
}
|
||||
|
||||
// Less compare two elements
|
||||
func (ls SortedList[T]) Less(i, j int) bool {
|
||||
return ls[i] < ls[j]
|
||||
}
|
||||
|
||||
// Swap elements by indexes
|
||||
func (ls SortedList[T]) Swap(i, j int) {
|
||||
ls[i], ls[j] = ls[j], ls[i]
|
||||
}
|
||||
|
||||
// IsEmpty check
|
||||
func (ls SortedList[T]) IsEmpty() bool {
|
||||
return len(ls) == 0
|
||||
}
|
||||
|
||||
// String to string
|
||||
func (ls SortedList[T]) String() string {
|
||||
return ToString(ls)
|
||||
}
|
||||
|
||||
// Has given element
|
||||
func (ls SortedList[T]) Has(el T) bool {
|
||||
return ls.Contains(el)
|
||||
}
|
||||
|
||||
// Contains given element
|
||||
func (ls SortedList[T]) Contains(el T) bool {
|
||||
for _, v := range ls {
|
||||
if v == el {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// First element value.
|
||||
func (ls SortedList[T]) First(defVal ...T) T {
|
||||
if len(ls) > 0 {
|
||||
return ls[0]
|
||||
}
|
||||
|
||||
if len(defVal) > 0 {
|
||||
return defVal[0]
|
||||
}
|
||||
panic("empty list")
|
||||
}
|
||||
|
||||
// Last element value.
|
||||
func (ls SortedList[T]) Last(defVal ...T) T {
|
||||
if ln := len(ls); ln > 0 {
|
||||
return ls[ln-1]
|
||||
}
|
||||
|
||||
if len(defVal) > 0 {
|
||||
return defVal[0]
|
||||
}
|
||||
panic("empty list")
|
||||
}
|
||||
|
||||
// Remove given element
|
||||
func (ls SortedList[T]) Remove(el T) SortedList[T] {
|
||||
return Filter(ls, func(v T) bool {
|
||||
return v != el
|
||||
})
|
||||
}
|
||||
|
||||
// Filter the slice, default will filter zero value.
|
||||
func (ls SortedList[T]) Filter(filter ...comdef.MatchFunc[T]) SortedList[T] {
|
||||
return Filter(ls, filter...)
|
||||
}
|
||||
|
||||
// Map the slice to new slice. TODO syntax ERROR: Method cannot have type parameters
|
||||
// func (ls SortedList[T]) Map[V any](mapFn MapFn[T, V]) SortedList[V] {
|
||||
// return Map(ls, mapFn)
|
||||
// }
|
||||
|
||||
// Sort the slice
|
||||
func (ls SortedList[T]) Sort() {
|
||||
sort.Sort(ls)
|
||||
}
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
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 {
|
||||
// if el == nil { // Filter nil value
|
||||
// return false
|
||||
// }
|
||||
return !reflect.ValueOf(el).IsZero()
|
||||
}
|
||||
}
|
||||
|
||||
newLs := make([]T, 0, len(ls))
|
||||
for _, el := range ls {
|
||||
if fn(el) {
|
||||
newLs = append(newLs, el)
|
||||
}
|
||||
}
|
||||
return newLs
|
||||
}
|
||||
|
||||
// Map a list to new list with map filter function.
|
||||
//
|
||||
// eg: mapping [object0{},object1{},...] to flatten list [object0.someKey, object1.someKey, ...]
|
||||
func Map[T, V any](list []T, mapFilter func(input T) (target V, ok bool)) []V {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
flatArr := make([]V, 0, len(list))
|
||||
for _, obj := range list {
|
||||
if target, ok := mapFilter(obj); ok {
|
||||
flatArr = append(flatArr, target)
|
||||
}
|
||||
}
|
||||
return flatArr
|
||||
}
|
||||
|
||||
// Map1 a list to new list with map function.
|
||||
//
|
||||
// eg: mapping [object0{},object1{},...] to flatten list [object0.someKey, object1.someKey, ...]
|
||||
func Map1[T, R any](list []T, mapFn func(t T) R) []R {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := make([]R, len(list))
|
||||
for i := range list {
|
||||
ret[i] = mapFn(list[i])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Column collect sub elements from list. alias of Map func
|
||||
//
|
||||
// Example:
|
||||
// list := []map[string]any{
|
||||
// {"id": 1, "name": "one", "age": 23},
|
||||
// {"id": 2, "name": "two", "age": 23},
|
||||
// {"id": 3, "name": "three", "age": 23},
|
||||
// }
|
||||
// names := arrutil.Column(list, func(el map[string]any) string {
|
||||
// return el["name"].(string)
|
||||
// })
|
||||
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 comdef.NumberOrString](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 comdef.NumberOrString](val T, list []T) int {
|
||||
for i, v := range list {
|
||||
if v == val {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// FirstOr get first value of slice, if slice is empty, return the default value.
|
||||
func FirstOr[T any](list []T, defVal ...T) T {
|
||||
if len(list) > 0 {
|
||||
return list[0]
|
||||
}
|
||||
|
||||
if len(defVal) > 0 {
|
||||
return defVal[0]
|
||||
}
|
||||
var zero T
|
||||
return zero
|
||||
}
|
||||
|
||||
// Chunk split slice to chunks by size.
|
||||
//
|
||||
// eg: [1,2,3,4,5,6,7,8,9,10] -> [[1,2,3,4], [5,6,7,8], [9,10]]
|
||||
func Chunk[T any](list []T, size int) [][]T {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ln := len(list)
|
||||
if ln == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
chunks := make([][]T, 0, ln/size+1)
|
||||
|
||||
for i := 0; i < ln; i += size {
|
||||
end := i + size
|
||||
if end > ln {
|
||||
end = ln
|
||||
}
|
||||
chunks = append(chunks, list[i:end])
|
||||
}
|
||||
|
||||
return chunks
|
||||
}
|
||||
|
||||
// ChunkBy split slice to chunks by size, and with custom chunk function.
|
||||
//
|
||||
// Example:
|
||||
// list := []map[string]any{
|
||||
// {"id": 1, "name": "one", "age": 23},
|
||||
// {"id": 2, "name": "two", "age": 23},
|
||||
// {"id": 3, "name": "three", "age": 23},
|
||||
// }
|
||||
// chunks := arrutil.ChunkBy(list, 2, func(el map[string]any) map[string]any {
|
||||
// return map[string]any{
|
||||
// "id": el["id"],
|
||||
// "name": el["name"],
|
||||
// }
|
||||
// })
|
||||
// Output: [
|
||||
// [{"id": 1, "name": "one"}, {"id": 2, "name": "two"}],
|
||||
// [{"id": 3, "name": "three"}]
|
||||
// ]
|
||||
func ChunkBy[T, R any](list []T, size int, mapFn func(el T) R) [][]R {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ln := len(list)
|
||||
if ln == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 计算需要的块数量
|
||||
numChunks := ln/size + 1
|
||||
if ln%size == 0 {
|
||||
numChunks = ln / size
|
||||
}
|
||||
|
||||
chunks := make([][]R, 0, numChunks)
|
||||
|
||||
for i := 0; i < ln; i += size {
|
||||
end := i + size
|
||||
if end > ln {
|
||||
end = ln
|
||||
}
|
||||
|
||||
// 创建当前块的切片
|
||||
currentChunk := make([]R, 0, size)
|
||||
for j := i; j < end; j++ {
|
||||
currentChunk = append(currentChunk, mapFn(list[j]))
|
||||
}
|
||||
chunks = append(chunks, currentChunk)
|
||||
}
|
||||
|
||||
return chunks
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package arrutil
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/comdef"
|
||||
)
|
||||
|
||||
// StringsToAnys convert []string to []any
|
||||
func StringsToAnys(ss []string) []any {
|
||||
args := make([]any, len(ss))
|
||||
for i, s := range ss {
|
||||
args[i] = s
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// StringsToSlice convert []string to []any. alias of StringsToAnys()
|
||||
func StringsToSlice(ss []string) []any {
|
||||
return StringsToAnys(ss)
|
||||
}
|
||||
|
||||
// StringsAsInts convert and ignore error
|
||||
func StringsAsInts(ss []string) []int {
|
||||
ints, _ := StringsTryInts(ss)
|
||||
return ints
|
||||
}
|
||||
|
||||
// StringsToInts string slice to int slice
|
||||
func StringsToInts(ss []string) (ints []int, err error) {
|
||||
return StringsTryInts(ss)
|
||||
}
|
||||
|
||||
// StringsTryInts string slice to int slice
|
||||
func StringsTryInts(ss []string) (ints []int, err error) {
|
||||
for _, str := range ss {
|
||||
iVal, err := strconv.Atoi(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ints = append(ints, iVal)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// StringsUnique unique string slice
|
||||
func StringsUnique(ss []string) []string {
|
||||
if len(ss) == 0 {
|
||||
return ss
|
||||
}
|
||||
|
||||
var unique []string
|
||||
for _, s := range ss {
|
||||
if !StringsContains(unique, s) {
|
||||
unique = append(unique, s)
|
||||
}
|
||||
}
|
||||
return unique
|
||||
}
|
||||
|
||||
// StringsContains check string slice contains string
|
||||
func StringsContains(ss []string, s string) bool {
|
||||
for _, v := range ss {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// StringsRemove value form a string slice
|
||||
func StringsRemove(ss []string, s string) []string {
|
||||
return StringsFilter(ss, func(el string) bool {
|
||||
return s != el
|
||||
})
|
||||
}
|
||||
|
||||
// StringsFilter given strings, default will filter emtpy string.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// // output: [a, b]
|
||||
// ss := arrutil.StringsFilter([]string{"a", "", "b", ""})
|
||||
func StringsFilter(ss []string, filter ...comdef.StringMatchFunc) []string {
|
||||
var fn comdef.StringMatchFunc
|
||||
if len(filter) > 0 && filter[0] != nil {
|
||||
fn = filter[0]
|
||||
} else {
|
||||
fn = func(s string) bool {
|
||||
return s != ""
|
||||
}
|
||||
}
|
||||
|
||||
ns := make([]string, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
if fn(s) {
|
||||
ns = append(ns, s)
|
||||
}
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
// StringsMap handle each string item, map to new strings
|
||||
func StringsMap(ss []string, mapFn func(s string) string) []string {
|
||||
ns := make([]string, 0, len(ss))
|
||||
for _, s := range ss {
|
||||
ns = append(ns, mapFn(s))
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
// TrimStrings trim string slice item.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// // output: [a, b, c]
|
||||
// ss := arrutil.TrimStrings([]string{",a", "b.", ",.c,"}, ",.")
|
||||
func TrimStrings(ss []string, cutSet ...string) []string {
|
||||
cutSetLn := len(cutSet)
|
||||
hasCutSet := cutSetLn > 0 && cutSet[0] != ""
|
||||
|
||||
var trimSet string
|
||||
if hasCutSet {
|
||||
trimSet = cutSet[0]
|
||||
}
|
||||
if cutSetLn > 1 {
|
||||
trimSet = strings.Join(cutSet, "")
|
||||
}
|
||||
|
||||
ns := make([]string, 0, len(ss))
|
||||
for _, str := range ss {
|
||||
if hasCutSet {
|
||||
ns = append(ns, strings.Trim(str, trimSet))
|
||||
} else {
|
||||
ns = append(ns, strings.TrimSpace(str))
|
||||
}
|
||||
}
|
||||
return ns
|
||||
}
|
||||
Reference in New Issue
Block a user