241
slicex/slice.go
Normal file
241
slicex/slice.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package slicex
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
gox "git.cloudyne.io/go/hiscaler-gox"
|
||||
)
|
||||
|
||||
// Map values all value execute f function, and return a new slice
|
||||
//
|
||||
// Example
|
||||
//
|
||||
// Map([]string{"A", "B", "C"}, func(v string) string {
|
||||
// return strings.ToLower(v)
|
||||
// })
|
||||
// // Output: ["a", "b", "c"]
|
||||
func Map[T comparable](values []T, f func(v T) T) []T {
|
||||
items := make([]T, len(values))
|
||||
for i, v := range values {
|
||||
items[i] = f(v)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
// Filter return matched f function condition value
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// Filter([]int{0, 1, 2, 3}, func(v int) bool {
|
||||
// return v > 0
|
||||
// })
|
||||
// // Output: [1, 2, 3]
|
||||
func Filter[T comparable](values []T, f func(v T) bool) []T {
|
||||
items := make([]T, 0)
|
||||
for _, v := range values {
|
||||
if ok := f(v); ok {
|
||||
items = append(items, v)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func ToInterface[T gox.Number | ~string](values []T) []interface{} {
|
||||
if values == nil || len(values) == 0 {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
ifs := make([]interface{}, len(values))
|
||||
for i, value := range values {
|
||||
ifs[i] = value
|
||||
}
|
||||
return ifs
|
||||
}
|
||||
|
||||
// StringToInterface Change string slice to interface slice
|
||||
func StringToInterface(values []string) []interface{} {
|
||||
return ToInterface(values)
|
||||
}
|
||||
|
||||
// IntToInterface Change int slice to interface slice
|
||||
func IntToInterface(values []int) []interface{} {
|
||||
return ToInterface(values)
|
||||
}
|
||||
|
||||
// StringSliceEqual Check a, b is equal
|
||||
func StringSliceEqual(a, b []string, caseSensitive, ignoreEmpty, trim bool) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
} else if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !caseSensitive || ignoreEmpty || trim {
|
||||
fixFunc := func(ss []string) []string {
|
||||
if len(ss) == 0 {
|
||||
return ss
|
||||
}
|
||||
values := make([]string, 0)
|
||||
for _, s := range ss {
|
||||
if trim {
|
||||
s = strings.TrimSpace(s)
|
||||
}
|
||||
if s == "" && ignoreEmpty {
|
||||
continue
|
||||
}
|
||||
if !caseSensitive {
|
||||
s = strings.ToUpper(s)
|
||||
}
|
||||
values = append(values, s)
|
||||
}
|
||||
return values
|
||||
}
|
||||
a = fixFunc(a)
|
||||
b = fixFunc(b)
|
||||
}
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, av := range a {
|
||||
exists := false
|
||||
for _, bv := range b {
|
||||
if av == bv {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IntSliceEqual Check a, b is equal
|
||||
func IntSliceEqual(a, b []int) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
} else if a == nil || b == nil || len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, av := range a {
|
||||
exists := false
|
||||
for _, bv := range b {
|
||||
if av == bv {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func StringSliceReverse(ss []string) []string {
|
||||
n := len(ss)
|
||||
if n <= 1 {
|
||||
return ss
|
||||
}
|
||||
|
||||
vv := make([]string, len(ss))
|
||||
copy(vv, ss)
|
||||
for k1 := 0; k1 < n/2; k1++ {
|
||||
k2 := n - k1 - 1
|
||||
vv[k1], vv[k2] = vv[k2], vv[k1]
|
||||
}
|
||||
return vv
|
||||
}
|
||||
|
||||
func IntSliceReverse(ss []int) []int {
|
||||
n := len(ss)
|
||||
if n <= 1 {
|
||||
return ss
|
||||
}
|
||||
|
||||
vv := make([]int, len(ss))
|
||||
copy(vv, ss)
|
||||
for k1 := 0; k1 < n/2; k1++ {
|
||||
k2 := n - k1 - 1
|
||||
vv[k1], vv[k2] = vv[k2], vv[k1]
|
||||
}
|
||||
return vv
|
||||
}
|
||||
|
||||
// Diff return a slice in ss[0] and not in ss[1:]
|
||||
func Diff[T comparable](values ...[]T) []T {
|
||||
diffValues := make([]T, 0)
|
||||
n := len(values)
|
||||
if n == 0 || values[0] == nil {
|
||||
return diffValues
|
||||
} else if n == 1 {
|
||||
return values[0]
|
||||
} else {
|
||||
items := make(map[T]struct{}, 0)
|
||||
for _, vs := range values[1:] {
|
||||
for _, v := range vs {
|
||||
items[v] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, v := range values[0] {
|
||||
if _, ok := items[v]; !ok {
|
||||
diffValues = append(diffValues, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return diffValues
|
||||
}
|
||||
|
||||
func StringSliceDiff(ss ...[]string) []string {
|
||||
diffValues := make([]string, 0)
|
||||
if len(ss) == 0 || ss[0] == nil {
|
||||
return diffValues
|
||||
} else if len(ss) == 1 {
|
||||
return ss[0]
|
||||
} else {
|
||||
for _, v1 := range ss[0] {
|
||||
exists := false
|
||||
for _, items := range ss[1:] {
|
||||
for _, v2 := range items {
|
||||
if strings.EqualFold(v1, v2) {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if exists {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
diffValues = append(diffValues, v1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return diffValues
|
||||
}
|
||||
|
||||
func IntSliceDiff(ss ...[]int) []int {
|
||||
return Diff(ss...)
|
||||
}
|
||||
|
||||
// Chunk chunks a slice by size
|
||||
func Chunk[T comparable](items []T, size int) [][]T {
|
||||
chunkItems := make([][]T, 0)
|
||||
n := len(items)
|
||||
if items == nil || n == 0 {
|
||||
return chunkItems
|
||||
} else if size <= 0 {
|
||||
return [][]T{items}
|
||||
}
|
||||
for i := 0; i < n; i += size {
|
||||
end := i + size
|
||||
if end > n {
|
||||
end = n
|
||||
}
|
||||
chunkItems = append(chunkItems, items[i:end])
|
||||
}
|
||||
return chunkItems
|
||||
}
|
||||
219
slicex/slice_test.go
Normal file
219
slicex/slice_test.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package slicex
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
a := []int{0, 1}
|
||||
b := Map(a, func(v int) int {
|
||||
return v + 1
|
||||
})
|
||||
assert.Equal(t, []int{0, 1}, a, "map.int")
|
||||
assert.Equal(t, []int{1, 2}, b, "map.int")
|
||||
|
||||
a1 := []string{"a", "b"}
|
||||
b1 := Map(a1, func(v string) string {
|
||||
return v + "1"
|
||||
})
|
||||
assert.Equal(t, []string{"a", "b"}, a1, "map.string")
|
||||
assert.Equal(t, []string{"a1", "b1"}, b1, "map.string")
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
assert.Equal(t, []int{1, 2, 3}, Filter([]int{0, 1, 2, 3}, func(v int) bool {
|
||||
return v > 0
|
||||
}), "map.int")
|
||||
assert.Equal(t, []string{"c"}, Filter([]string{"a", "b", "c", "a", "b"}, func(v string) bool {
|
||||
return v == "c"
|
||||
}), "map.string")
|
||||
}
|
||||
|
||||
func TestStringToInterface(t *testing.T) {
|
||||
tests := []struct {
|
||||
tag string
|
||||
input []string
|
||||
expected []interface{}
|
||||
}{
|
||||
{"t0", []string{"a", "b", "c"}, []interface{}{"a", "b", "c"}},
|
||||
{"t1", nil, []interface{}{}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
v := StringToInterface(test.input)
|
||||
assert.Equal(t, test.expected, v, test.tag)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringToInterface(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
StringToInterface([]string{"a", "b", "c"})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceEqual(t *testing.T) {
|
||||
testCases := []struct {
|
||||
A []string
|
||||
B []string
|
||||
CaseSensitive bool
|
||||
IgnoreEmpty bool
|
||||
Trim bool
|
||||
Except bool
|
||||
}{
|
||||
{[]string{}, []string{}, false, true, true, true},
|
||||
{[]string{"a", "b", "c"}, []string{"a", "b", "c"}, true, false, true, true},
|
||||
{[]string{"a", "b", "c"}, []string{"a", "b ", " c"}, true, false, true, true},
|
||||
{[]string{"a", "b", "c"}, []string{"a", "b ", " c"}, true, false, false, false},
|
||||
{[]string{"a", "b", "c", ""}, []string{"a", "b ", " c"}, true, false, true, false},
|
||||
{[]string{"a", "b", "c", ""}, []string{"a", "b ", " c"}, true, true, true, true},
|
||||
{[]string{"a", "b", "c", ""}, []string{"a", "b ", " c", ""}, true, false, true, true},
|
||||
{[]string{"A", "B", "C"}, []string{"a", "b", "c"}, true, true, true, false},
|
||||
{[]string{"A", "B", "C"}, []string{"a", "b", "c"}, false, true, true, true},
|
||||
{[]string{"A", "B", "C"}, []string{"b", "c", "a"}, false, true, true, true},
|
||||
{[]string{" ", "", " "}, []string{""}, false, true, true, true},
|
||||
{[]string{}, []string{" ", ""}, false, true, true, true},
|
||||
{[]string{}, []string{"a", "b"}, false, true, true, false},
|
||||
{nil, []string{}, false, true, true, false},
|
||||
{[]string{}, nil, false, true, true, false},
|
||||
{nil, nil, false, true, true, true},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
equal := StringSliceEqual(testCase.A, testCase.B, testCase.CaseSensitive, testCase.IgnoreEmpty, testCase.Trim)
|
||||
if equal != testCase.Except {
|
||||
t.Errorf("%d except %v actual %v", i, testCase.Except, equal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntSliceEqual(t *testing.T) {
|
||||
testCases := []struct {
|
||||
A []int
|
||||
B []int
|
||||
Except bool
|
||||
}{
|
||||
{[]int{}, []int{}, true},
|
||||
{[]int{0, 1, 2}, []int{0, 1, 2}, true},
|
||||
{[]int{0, 1, 2}, []int{2, 1, 0}, true},
|
||||
{[]int{0, 1, 2}, []int{1, 2}, false},
|
||||
{[]int{0, 1, 1, 2}, []int{0, 1, 2}, false},
|
||||
{[]int{0, 1, 1, 2}, []int{0, 1, 2, 1}, true},
|
||||
{nil, []int{}, false},
|
||||
{nil, nil, true},
|
||||
{[]int{}, nil, false},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
equal := IntSliceEqual(testCase.A, testCase.B)
|
||||
if equal != testCase.Except {
|
||||
t.Errorf("%d except %v actual %v", i, testCase.Except, equal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceReverse(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Before []string
|
||||
After []string
|
||||
}{
|
||||
{[]string{"a"}, []string{"a"}},
|
||||
{[]string{"a", "b"}, []string{"b", "a"}},
|
||||
{[]string{"a", "b", "c"}, []string{"c", "b", "a"}},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
values := StringSliceReverse(testCase.Before)
|
||||
if len(values) != len(testCase.After) {
|
||||
t.Errorf("%#v reverse after value except: %#v, actual: %#v", testCase.Before, testCase.After, values)
|
||||
} else {
|
||||
for j, v := range values {
|
||||
if testCase.After[j] != v {
|
||||
t.Errorf("%#v reverse after value except: %#v, actual: %#v", testCase.Before, testCase.After, values)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceDiff(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Number int
|
||||
OriginalValues [][]string
|
||||
DiffValue []string
|
||||
}{
|
||||
{1, [][]string{{"a", "b", "c"}, {"a", "b", "d"}}, []string{"c"}},
|
||||
{1, [][]string{{"a", "b", "c"}, {"a"}}, []string{"b", "c"}},
|
||||
{2, [][]string{{"a", "b", "d"}, {"a", "b", "c"}}, []string{"d"}},
|
||||
{3, [][]string{{"a", "b", "c"}, {"a", "b", "c"}}, []string{}},
|
||||
{4, [][]string{{"a", "b", ""}, {"a", "b", "c"}}, []string{""}},
|
||||
{5, [][]string{{"a", "b", "c"}, {"a", "b"}, {"c"}}, []string{}},
|
||||
{6, [][]string{{"a"}, {"b"}, {"c", "c1"}, {"d"}}, []string{"a"}},
|
||||
{7, [][]string{nil, {"a"}, {"b"}, {"c", "c1"}, {"d"}}, []string{}},
|
||||
{8, [][]string{nil}, []string{}},
|
||||
{9, [][]string{nil, nil, nil}, []string{}},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
values := StringSliceDiff(testCase.OriginalValues...)
|
||||
if !StringSliceEqual(values, testCase.DiffValue, true, false, true) {
|
||||
t.Errorf("%d: diff values except: %#v, actual: %#v", testCase.Number, testCase.DiffValue, values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntSliceDiff(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Number int
|
||||
OriginalValues [][]int
|
||||
DiffValue []int
|
||||
}{
|
||||
{1, [][]int{{1, 2, 3}, {1, 2, 4}}, []int{3}},
|
||||
{2, [][]int{{1, 2, 3}, {1, 2, 2, 3}, {3, 4, 5}}, []int{}},
|
||||
{3, [][]int{{1, 2, 3}, {1}, {2}, {3}}, []int{}},
|
||||
{4, [][]int{{1, 2, 3}, {1, 2, 4, 0, 2, 1}}, []int{3}},
|
||||
{5, [][]int{{1, 2, 2, 3}, {1}}, []int{2, 2, 3}},
|
||||
{6, [][]int{}, []int{}},
|
||||
{7, [][]int{nil, {1, 2, 3}}, []int{}},
|
||||
{8, [][]int{nil, nil, {1, 2, 3}}, []int{}},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
values := IntSliceDiff(testCase.OriginalValues...)
|
||||
if !IntSliceEqual(values, testCase.DiffValue) {
|
||||
t.Errorf("%d: diff values except: %#v, actual: %#v", testCase.Number, testCase.DiffValue, values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToInterface(t *testing.T) {
|
||||
type args struct {
|
||||
values interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []interface{}
|
||||
}{
|
||||
{"t1", args{[]int{1, 2, 3}}, []interface{}{1, 2, 3}},
|
||||
{"t2", args{[]string{"A", "B", "C"}}, []interface{}{"A", "B", "C"}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var value interface{}
|
||||
switch tt.args.values.(type) {
|
||||
case []string:
|
||||
value = ToInterface(tt.args.values.([]string))
|
||||
case []int:
|
||||
value = ToInterface(tt.args.values.([]int))
|
||||
}
|
||||
assert.Equalf(t, tt.want, value, "ToInterface(%v)", tt.args.values)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChunk(t *testing.T) {
|
||||
assert.ElementsMatch(t, Chunk([]int{1, 2, 3}, 0), [][]int{{1, 2, 3}}, "int0")
|
||||
assert.ElementsMatch(t, Chunk([]int{1, 2, 3}, 1), [][]int{{1}, {2}, {3}}, "int1")
|
||||
assert.ElementsMatch(t, Chunk([]int{1, 2, 3}, 2), [][]int{{1, 2}, {3}}, "int2")
|
||||
assert.ElementsMatch(t, Chunk([]int{1, 2, 3}, 3), [][]int{{1, 2, 3}}, "int3")
|
||||
}
|
||||
Reference in New Issue
Block a user