diff --git a/utils/collection/convert.go b/utils/collection/convert.go index 4333f66..63df650 100644 --- a/utils/collection/convert.go +++ b/utils/collection/convert.go @@ -2,7 +2,7 @@ package collection // ConvertSliceToAny 将切片转换为任意类型的切片 func ConvertSliceToAny[S ~[]V, V any](s S) []any { - if s == nil { + if len(s) == 0 { return nil } var r = make([]any, len(s)) @@ -14,8 +14,8 @@ func ConvertSliceToAny[S ~[]V, V any](s S) []any { // ConvertSliceToIndexMap 将切片转换为索引为键的映射 func ConvertSliceToIndexMap[S ~[]V, V any](s S) map[int]V { - if s == nil { - return nil + if len(s) == 0 { + return make(map[int]V) } var r = make(map[int]V, len(s)) for i, v := range s { @@ -24,9 +24,21 @@ func ConvertSliceToIndexMap[S ~[]V, V any](s S) map[int]V { return r } +// ConvertSliceToIndexOnlyMap 将切片转换为索引为键的映射 +func ConvertSliceToIndexOnlyMap[S ~[]V, V any](s S) map[int]struct{} { + if len(s) == 0 { + return nil + } + var r = make(map[int]struct{}, len(s)) + for i := range s { + r[i] = struct{}{} + } + return r +} + // ConvertSliceToMap 将切片转换为值为键的映射 func ConvertSliceToMap[S ~[]V, V comparable](s S) map[V]struct{} { - if s == nil { + if len(s) == 0 { return nil } var r = make(map[V]struct{}, len(s)) @@ -38,8 +50,8 @@ func ConvertSliceToMap[S ~[]V, V comparable](s S) map[V]struct{} { // ConvertSliceToBoolMap 将切片转换为值为键的映射 func ConvertSliceToBoolMap[S ~[]V, V comparable](s S) map[V]bool { - if s == nil { - return nil + if len(s) == 0 { + return make(map[V]bool) } var r = make(map[V]bool, len(s)) for _, v := range s { @@ -50,7 +62,7 @@ func ConvertSliceToBoolMap[S ~[]V, V comparable](s S) map[V]bool { // ConvertMapKeysToSlice 将映射的键转换为切片 func ConvertMapKeysToSlice[M ~map[K]V, K comparable, V any](m M) []K { - if m == nil { + if len(m) == 0 { return nil } var r = make([]K, 0, len(m)) @@ -62,7 +74,7 @@ func ConvertMapKeysToSlice[M ~map[K]V, K comparable, V any](m M) []K { // ConvertMapValuesToSlice 将映射的值转换为切片 func ConvertMapValuesToSlice[M ~map[K]V, K comparable, V any](m M) []V { - if m == nil { + if len(m) == 0 { return nil } var r = make([]V, 0, len(m)) @@ -73,7 +85,7 @@ func ConvertMapValuesToSlice[M ~map[K]V, K comparable, V any](m M) []V { } // InvertMap 将映射的键和值互换 -func InvertMap[M ~map[K]V, N ~map[V]K, K, V comparable](m M) N { +func InvertMap[M ~map[K]V, N map[V]K, K, V comparable](m M) N { if m == nil { return nil } @@ -85,7 +97,7 @@ func InvertMap[M ~map[K]V, N ~map[V]K, K, V comparable](m M) N { } // ConvertMapValuesToBool 将映射的值转换为布尔值 -func ConvertMapValuesToBool[M ~map[K]V, N ~map[K]bool, K comparable, V any](m M) N { +func ConvertMapValuesToBool[M ~map[K]V, N map[K]bool, K comparable, V any](m M) N { if m == nil { return nil } diff --git a/utils/collection/convert_example_test.go b/utils/collection/convert_example_test.go index 10c48ea..3658224 100644 --- a/utils/collection/convert_example_test.go +++ b/utils/collection/convert_example_test.go @@ -1 +1,104 @@ package collection_test + +import ( + "fmt" + "github.com/kercylan98/minotaur/utils/collection" + "reflect" +) + +func ExampleConvertSliceToAny() { + result := collection.ConvertSliceToAny([]int{1, 2, 3}) + fmt.Println(reflect.TypeOf(result).String(), len(result)) + // Output: + // []interface {} 3 +} + +func ExampleConvertSliceToIndexMap() { + slice := []int{1, 2, 3} + result := collection.ConvertSliceToIndexMap(slice) + for i, v := range slice { + fmt.Println(result[i], v) + } + // Output: + // 1 1 + // 2 2 + // 3 3 +} + +func ExampleConvertSliceToIndexOnlyMap() { + slice := []int{1, 2, 3} + result := collection.ConvertSliceToIndexOnlyMap(slice) + expected := map[int]bool{0: true, 1: true, 2: true} + for k := range result { + fmt.Println(expected[k]) + } + // Output: + // true + // true + // true +} + +func ExampleConvertSliceToMap() { + slice := []int{1, 2, 3} + result := collection.ConvertSliceToMap(slice) + fmt.Println(collection.AllKeyInMap(result, slice...)) + // Output: + // true +} + +func ExampleConvertSliceToBoolMap() { + slice := []int{1, 2, 3} + result := collection.ConvertSliceToBoolMap(slice) + for _, v := range slice { + fmt.Println(v, result[v]) + } + // Output: + // 1 true + // 2 true + // 3 true +} + +func ExampleConvertMapKeysToSlice() { + result := collection.ConvertMapKeysToSlice(map[int]int{1: 1, 2: 2, 3: 3}) + for i, v := range result { + fmt.Println(i, v) + } + // Output: + // 0 1 + // 1 2 + // 2 3 +} + +func ExampleConvertMapValuesToSlice() { + result := collection.ConvertMapValuesToSlice(map[int]int{1: 1, 2: 2, 3: 3}) + expected := map[int]bool{1: true, 2: true, 3: true} + for _, v := range result { + fmt.Println(expected[v]) + } + // Output: + // true + // true + // true +} + +func ExampleInvertMap() { + result := collection.InvertMap(map[int]string{1: "a", 2: "b", 3: "c"}) + fmt.Println(collection.AllKeyInMap(result, "a", "b", "c")) + // Output: + // true +} + +func ExampleConvertMapValuesToBool() { + result := collection.ConvertMapValuesToBool(map[int]int{1: 1}) + fmt.Println(result) + // Output: + // map[1:true] +} + +func ExampleReverseSlice() { + var s = []int{1, 2, 3} + collection.ReverseSlice(&s) + fmt.Println(s) + // Output: + // [3 2 1] +} diff --git a/utils/collection/convert_test.go b/utils/collection/convert_test.go index 006a8a1..8b875eb 100644 --- a/utils/collection/convert_test.go +++ b/utils/collection/convert_test.go @@ -62,6 +62,35 @@ func TestConvertSliceToIndexMap(t *testing.T) { } } +func TestConvertSliceToIndexOnlyMap(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]struct{} + }{ + {name: "TestConvertSliceToIndexOnlyMap_NonEmpty", input: []int{1, 2, 3}, expected: map[int]struct{}{0: {}, 1: {}, 2: {}}}, + {name: "TestConvertSliceToIndexOnlyMap_Empty", input: []int{}, expected: map[int]struct{}{}}, + {name: "TestConvertSliceToIndexOnlyMap_Nil", input: nil, expected: nil}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertSliceToIndexOnlyMap(c.input) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for k, v := range actual { + if _, ok := c.expected[k]; !ok { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + if v != struct{}{} { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + }) + } +} + func TestConvertSliceToMap(t *testing.T) { var cases = []struct { name string diff --git a/utils/collection/merge.go b/utils/collection/merge.go index 89a4757..f0f78f3 100644 --- a/utils/collection/merge.go +++ b/utils/collection/merge.go @@ -21,7 +21,7 @@ func MergeSlices[S ~[]V, V any](slices ...S) (result S) { // MergeMaps 合并 map,当多个 map 中存在相同的 key 时,后面的 map 中的 key 将会覆盖前面的 map 中的 key func MergeMaps[M ~map[K]V, K comparable, V any](maps ...M) (result M) { if len(maps) == 0 { - return nil + return make(M) } var length int diff --git a/utils/collection/merge_example_test.go b/utils/collection/merge_example_test.go new file mode 100644 index 0000000..8319152 --- /dev/null +++ b/utils/collection/merge_example_test.go @@ -0,0 +1,37 @@ +package collection_test + +import ( + "fmt" + "github.com/kercylan98/minotaur/utils/collection" +) + +func ExampleMergeSlices() { + fmt.Println( + collection.MergeSlices( + []int{1, 2, 3}, + []int{4, 5, 6}, + ), + ) + // Output: + // [1 2 3 4 5 6] +} + +func ExampleMergeMaps() { + m := collection.MergeMaps( + map[int]int{1: 1, 2: 2, 3: 3}, + map[int]int{4: 4, 5: 5, 6: 6}, + ) + fmt.Println(len(m)) + // Output: + // 6 +} + +func ExampleMergeMapsWithSkip() { + m := collection.MergeMapsWithSkip( + map[int]int{1: 1}, + map[int]int{1: 2}, + ) + fmt.Println(m[1]) + // Output: + // 1 +} diff --git a/utils/collection/merge_test.go b/utils/collection/merge_test.go new file mode 100644 index 0000000..ea31c64 --- /dev/null +++ b/utils/collection/merge_test.go @@ -0,0 +1,71 @@ +package collection_test + +import ( + "github.com/kercylan98/minotaur/utils/collection" + "testing" +) + +func TestMergeSlices(t *testing.T) { + var cases = []struct { + name string + input [][]int + expected []int + }{ + {"TestMergeSlices_NonEmptySlice", [][]int{{1, 2, 3}, {4, 5, 6}}, []int{1, 2, 3, 4, 5, 6}}, + {"TestMergeSlices_EmptySlice", [][]int{{}, {}}, []int{}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.MergeSlices(c.input...) + if len(result) != len(c.expected) { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + for i := 0; i < len(result); i++ { + if result[i] != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the value of input is not equal") + } + } + }) + } +} + +func TestMergeMaps(t *testing.T) { + var cases = []struct { + name string + input []map[int]int + expected int + }{ + {"TestMergeMaps_NonEmptyMap", []map[int]int{{1: 1, 2: 2, 3: 3}, {4: 4, 5: 5, 6: 6}}, 6}, + {"TestMergeMaps_EmptyMap", []map[int]int{{}, {}}, 0}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.MergeMaps(c.input...) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestMergeMapsWithSkip(t *testing.T) { + var cases = []struct { + name string + input []map[int]int + expected int + }{ + {"TestMergeMapsWithSkip_NonEmptyMap", []map[int]int{{1: 1}, {1: 2}}, 1}, + {"TestMergeMapsWithSkip_EmptyMap", []map[int]int{{}, {}}, 0}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.MergeMapsWithSkip(c.input...) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} diff --git a/utils/collection/random.go b/utils/collection/random.go index 567eec0..cdb8816 100644 --- a/utils/collection/random.go +++ b/utils/collection/random.go @@ -5,74 +5,71 @@ import ( "github.com/kercylan98/minotaur/utils/random" ) -// ChooseRandomSliceElementRepeatN 获取切片中的 inputN 个随机元素,允许重复 -// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic +// ChooseRandomSliceElementRepeatN 返回 slice 中的 n 个可重复随机元素 +// - 当 slice 长度为 0 或 n 小于等于 0 时将会返回 nil func ChooseRandomSliceElementRepeatN[S ~[]V, V any](slice S, n int) (result []V) { - if slice == nil { + if len(slice) == 0 || n <= 0 { return } - if n > len(slice) || n < 0 { - panic(fmt.Errorf("inputN is greater than the length of the input or less than 0, inputN: %d, length: %d", n, len(slice))) - } result = make([]V, n) + m := len(slice) - 1 for i := 0; i < n; i++ { - result[i] = slice[random.Int(0, len(slice)-1)] + result[i] = slice[random.Int(0, m)] } return } -// ChooseRandomIndexRepeatN 获取切片中的 inputN 个随机元素的索引,允许重复 -// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic +// ChooseRandomIndexRepeatN 返回 slice 中的 n 个可重复随机元素的索引 +// - 当 slice 长度为 0 或 n 小于等于 0 时将会返回 nil func ChooseRandomIndexRepeatN[S ~[]V, V any](slice S, n int) (result []int) { - if slice == nil { + if len(slice) == 0 || n <= 0 { return } - if n > len(slice) || n < 0 { - panic(fmt.Errorf("inputN is greater than the length of the input or less than 0, inputN: %d, length: %d", n, len(slice))) - } result = make([]int, n) + m := len(slice) - 1 for i := 0; i < n; i++ { - result[i] = random.Int(0, len(slice)-1) + result[i] = random.Int(0, m) } return } -// ChooseRandomSliceElement 获取切片中的随机元素 +// ChooseRandomSliceElement 返回 slice 中随机一个元素,当 slice 长度为 0 时将会得到 V 的零值 func ChooseRandomSliceElement[S ~[]V, V any](slice S) (v V) { - if slice == nil { + if len(slice) == 0 { return } return slice[random.Int(0, len(slice)-1)] } -// ChooseRandomIndex 获取切片中的随机元素的索引 +// ChooseRandomIndex 返回 slice 中随机一个元素的索引,当 slice 长度为 0 时将会得到 -1 func ChooseRandomIndex[S ~[]V, V any](slice S) (index int) { - if slice == nil { - return + if len(slice) == 0 { + return -1 } return random.Int(0, len(slice)-1) } -// ChooseRandomSliceElementN 获取切片中的 inputN 个随机元素 -// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic +// ChooseRandomSliceElementN 返回 slice 中的 n 个不可重复的随机元素 +// - 当 slice 长度为 0 或 n 大于 slice 长度或小于 0 时将会发生 panic func ChooseRandomSliceElementN[S ~[]V, V any](slice S, n int) (result []V) { - if slice == nil { - return + if len(slice) == 0 || n <= 0 || n > len(slice) { + panic(fmt.Errorf("n is greater than the length of the slice or less than 0, n: %d, length: %d", n, len(slice))) } - if n > len(slice) || n < 0 { - panic(fmt.Errorf("inputN is greater than the length of the input or less than 0, inputN: %d, length: %d", n, len(slice))) - } - result = make([]V, n) - for i := 0; i < n; i++ { - result[i] = slice[random.Int(0, len(slice)-1)] + result = make([]V, 0, n) + valid := ConvertSliceToIndexOnlyMap(slice) + for i := range valid { + result = append(result, slice[i]) + if len(result) == n { + break + } } return } -// ChooseRandomIndexN 获取切片中的 inputN 个随机元素的索引 -// - 如果 inputN 大于切片长度或小于 0 时将会发生 panic +// ChooseRandomIndexN 获取切片中的 n 个随机元素的索引 +// - 如果 n 大于切片长度或小于 0 时将会发生 panic func ChooseRandomIndexN[S ~[]V, V any](slice S, n int) (result []int) { - if slice == nil { + if len(slice) == 0 { return } if n > len(slice) || n < 0 { @@ -85,8 +82,8 @@ func ChooseRandomIndexN[S ~[]V, V any](slice S, n int) (result []int) { return } -// ChooseRandomMapKeyRepeatN 获取 map 中的 inputN 个随机 key,允许重复 -// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic +// ChooseRandomMapKeyRepeatN 获取 map 中的 n 个随机 key,允许重复 +// - 如果 n 大于 map 长度或小于 0 时将会发生 panic func ChooseRandomMapKeyRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (result []K) { if m == nil { return @@ -104,8 +101,8 @@ func ChooseRandomMapKeyRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (res return } -// ChooseRandomMapValueRepeatN 获取 map 中的 inputN 个随机 inputV,允许重复 -// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic +// ChooseRandomMapValueRepeatN 获取 map 中的 n 个随机 inputV,允许重复 +// - 如果 n 大于 map 长度或小于 0 时将会发生 panic func ChooseRandomMapValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (result []V) { if m == nil { return @@ -123,8 +120,8 @@ func ChooseRandomMapValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) (r return } -// ChooseRandomMapKeyAndValueRepeatN 获取 map 中的 inputN 个随机 key 和 inputV,允许重复 -// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic +// ChooseRandomMapKeyAndValueRepeatN 获取 map 中的 n 个随机 key 和 v,允许重复 +// - 如果 n 大于 map 长度或小于 0 时将会发生 panic func ChooseRandomMapKeyAndValueRepeatN[M ~map[K]V, K comparable, V any](m M, n int) M { if m == nil { return nil @@ -206,7 +203,7 @@ func ChooseRandomMapValueN[M ~map[K]V, K comparable, V any](m M, n int) (result return } -// ChooseRandomMapKeyAndValue 获取 map 中的随机 key 和 inputV +// ChooseRandomMapKeyAndValue 获取 map 中的随机 key 和 v func ChooseRandomMapKeyAndValue[M ~map[K]V, K comparable, V any](m M) (k K, v V) { if m == nil { return @@ -217,8 +214,8 @@ func ChooseRandomMapKeyAndValue[M ~map[K]V, K comparable, V any](m M) (k K, v V) return } -// ChooseRandomMapKeyAndValueN 获取 map 中的 inputN 个随机 key 和 inputV -// - 如果 inputN 大于 map 长度或小于 0 时将会发生 panic +// ChooseRandomMapKeyAndValueN 获取 map 中的 inputN 个随机 key 和 v +// - 如果 n 大于 map 长度或小于 0 时将会发生 panic func ChooseRandomMapKeyAndValueN[M ~map[K]V, K comparable, V any](m M, n int) M { if m == nil { return nil diff --git a/utils/collection/random_example_test.go b/utils/collection/random_example_test.go new file mode 100644 index 0000000..d19a7d5 --- /dev/null +++ b/utils/collection/random_example_test.go @@ -0,0 +1,111 @@ +package collection_test + +import ( + "fmt" + "github.com/kercylan98/minotaur/utils/collection" +) + +func ExampleChooseRandomSliceElementRepeatN() { + result := collection.ChooseRandomSliceElementRepeatN([]int{1}, 10) + fmt.Println(result) + // Output: + // [1 1 1 1 1 1 1 1 1 1] +} + +func ExampleChooseRandomIndexRepeatN() { + result := collection.ChooseRandomIndexRepeatN([]int{1}, 10) + fmt.Println(result) + // Output: + // [0 0 0 0 0 0 0 0 0 0] +} + +func ExampleChooseRandomSliceElement() { + result := collection.ChooseRandomSliceElement([]int{1}) + fmt.Println(result) + // Output: + // 1 +} + +func ExampleChooseRandomIndex() { + result := collection.ChooseRandomIndex([]int{1}) + fmt.Println(result) + // Output: + // 0 +} + +func ExampleChooseRandomSliceElementN() { + result := collection.ChooseRandomSliceElementN([]int{1}, 1) + fmt.Println(result) + // Output: + // [1] +} + +func ExampleChooseRandomIndexN() { + result := collection.ChooseRandomIndexN([]int{1}, 1) + fmt.Println(result) + // Output: + // [0] +} + +func ExampleChooseRandomMapKeyRepeatN() { + result := collection.ChooseRandomMapKeyRepeatN(map[int]int{1: 1}, 1) + fmt.Println(result) + // Output: + // [1] +} + +func ExampleChooseRandomMapValueRepeatN() { + result := collection.ChooseRandomMapValueRepeatN(map[int]int{1: 1}, 1) + fmt.Println(result) + // Output: + // [1] +} + +func ExampleChooseRandomMapKeyAndValueRepeatN() { + result := collection.ChooseRandomMapKeyAndValueRepeatN(map[int]int{1: 1}, 1) + fmt.Println(result) + // Output: + // map[1:1] +} + +func ExampleChooseRandomMapKey() { + result := collection.ChooseRandomMapKey(map[int]int{1: 1}) + fmt.Println(result) + // Output: + // 1 +} + +func ExampleChooseRandomMapValue() { + result := collection.ChooseRandomMapValue(map[int]int{1: 1}) + fmt.Println(result) + // Output: + // 1 +} + +func ExampleChooseRandomMapKeyN() { + result := collection.ChooseRandomMapKeyN(map[int]int{1: 1}, 1) + fmt.Println(result) + // Output: + // [1] +} + +func ExampleChooseRandomMapValueN() { + result := collection.ChooseRandomMapValueN(map[int]int{1: 1}, 1) + fmt.Println(result) + // Output: + // [1] +} + +func ExampleChooseRandomMapKeyAndValue() { + k, v := collection.ChooseRandomMapKeyAndValue(map[int]int{1: 1}) + fmt.Println(k, v) + // Output: + // 1 1 +} + +func ExampleChooseRandomMapKeyAndValueN() { + result := collection.ChooseRandomMapKeyAndValueN(map[int]int{1: 1}, 1) + fmt.Println(result) + // Output: + // map[1:1] +} diff --git a/utils/collection/random_test.go b/utils/collection/random_test.go new file mode 100644 index 0000000..27a6e1b --- /dev/null +++ b/utils/collection/random_test.go @@ -0,0 +1,280 @@ +package collection_test + +import ( + "github.com/kercylan98/minotaur/utils/collection" + "testing" +) + +func TestChooseRandomSliceElementRepeatN(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{ + {"TestChooseRandomSliceElementRepeatN_NonEmptySlice", []int{1, 2, 3}, 3}, + {"TestChooseRandomSliceElementRepeatN_EmptySlice", []int{}, 0}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomSliceElementRepeatN(c.input, 3) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomIndexRepeatN(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{ + {"TestChooseRandomIndexRepeatN_NonEmptySlice", []int{1, 2, 3}, 3}, + {"TestChooseRandomIndexRepeatN_EmptySlice", []int{}, 0}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomIndexRepeatN(c.input, 3) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomSliceElement(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]bool + }{ + {"TestChooseRandomSliceElement_NonEmptySlice", []int{1, 2, 3}, map[int]bool{1: true, 2: true, 3: true}}, + {"TestChooseRandomSliceElement_EmptySlice", []int{}, map[int]bool{0: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomSliceElement(c.input) + if !c.expected[result] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomIndex(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]bool + }{ + {"TestChooseRandomIndex_NonEmptySlice", []int{1, 2, 3}, map[int]bool{0: true, 1: true, 2: true}}, + {"TestChooseRandomIndex_EmptySlice", []int{}, map[int]bool{-1: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomIndex(c.input) + if !c.expected[result] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomSliceElementN(t *testing.T) { + var cases = []struct { + name string + input []int + }{ + {"TestChooseRandomSliceElementN_NonEmptySlice", []int{1, 2, 3}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ChooseRandomSliceElementN(c.input, 3) + if !collection.AllInComparableSlice(actual, c.input) { + t.Fatalf("%s failed, actual: %v, error: %s", c.name, actual, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomIndexN(t *testing.T) { + var cases = []struct { + name string + input []int + expected map[int]bool + }{ + {"TestChooseRandomIndexN_NonEmptySlice", []int{1, 2, 3}, map[int]bool{0: true, 1: true, 2: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomIndexN(c.input, 3) + if !c.expected[result[0]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomMapKeyRepeatN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{ + {"TestChooseRandomMapKeyRepeatN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapKeyRepeatN(c.input, 3) + if !c.expected[result[0]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomMapValueRepeatN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{ + {"TestChooseRandomMapValueRepeatN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapValueRepeatN(c.input, 3) + if !c.expected[result[0]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomMapKeyAndValueRepeatN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{ + {"TestChooseRandomMapKeyAndValueRepeatN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapKeyAndValueRepeatN(c.input, 3) + if !c.expected[result[1]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomMapKey(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{ + {"TestChooseRandomMapKey_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, + {"TestChooseRandomMapKey_EmptyMap", map[int]int{}, map[int]bool{0: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapKey(c.input) + if !c.expected[result] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomMapValue(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{ + {"TestChooseRandomMapValue_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, + {"TestChooseRandomMapValue_EmptyMap", map[int]int{}, map[int]bool{0: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapValue(c.input) + if !c.expected[result] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomMapValueN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{ + {"TestChooseRandomMapValueN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ChooseRandomMapValueN(c.input, 3) + if !c.expected[result[0]] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, result, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomMapKeyAndValue(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{ + {"TestChooseRandomMapKeyAndValue_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, + {"TestChooseRandomMapKeyAndValue_EmptyMap", map[int]int{}, map[int]bool{0: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + k, v := collection.ChooseRandomMapKeyAndValue(c.input) + if !c.expected[k] || !c.expected[v] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, k, "the length of input is not equal") + } + }) + } +} + +func TestChooseRandomMapKeyAndValueN(t *testing.T) { + var cases = []struct { + name string + input map[int]int + expected map[int]bool + }{ + {"TestChooseRandomMapKeyAndValueN_NonEmptyMap", map[int]int{1: 1, 2: 2, 3: 3}, map[int]bool{1: true, 2: true, 3: true}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + kvm := collection.ChooseRandomMapKeyAndValueN(c.input, 1) + for k := range kvm { + if !c.expected[k] { + t.Fatalf("%s failed, expected: %v, actual: %v, error: %s", c.name, c.expected, k, "the length of input is not equal") + } + } + }) + } +} diff --git a/utils/collection/sort_example_test.go b/utils/collection/sort_example_test.go new file mode 100644 index 0000000..be6164d --- /dev/null +++ b/utils/collection/sort_example_test.go @@ -0,0 +1,83 @@ +package collection_test + +import ( + "fmt" + "github.com/kercylan98/minotaur/utils/collection" + "sort" +) + +func ExampleDescBy() { + var slice = []int{1, 2, 3} + sort.Slice(slice, func(i, j int) bool { + return collection.DescBy(slice[i], slice[j]) + }) + fmt.Println(slice) + // Output: + // [3 2 1] +} + +func ExampleAscBy() { + var slice = []int{1, 2, 3} + sort.Slice(slice, func(i, j int) bool { + return collection.AscBy(slice[i], slice[j]) + }) + fmt.Println(slice) + // Output: + // [1 2 3] +} + +func ExampleDesc() { + var slice = []int{1, 2, 3} + collection.Desc(&slice, func(index int) int { + return slice[index] + }) + fmt.Println(slice) + // Output: + // [3 2 1] +} + +func ExampleDescByClone() { + var slice = []int{1, 2, 3} + result := collection.DescByClone(slice, func(index int) int { + return slice[index] + }) + fmt.Println(result) + // Output: + // [3 2 1] +} + +func ExampleAsc() { + var slice = []int{1, 2, 3} + collection.Asc(&slice, func(index int) int { + return slice[index] + }) + fmt.Println(slice) + // Output: + // [1 2 3] +} + +func ExampleAscByClone() { + var slice = []int{1, 2, 3} + result := collection.AscByClone(slice, func(index int) int { + return slice[index] + }) + fmt.Println(result) + // Output: + // [1 2 3] +} + +func ExampleShuffle() { + var slice = []int{1, 2, 3} + collection.Shuffle(&slice) + fmt.Println(len(slice)) + // Output: + // 3 +} + +func ExampleShuffleByClone() { + var slice = []int{1, 2, 3} + result := collection.ShuffleByClone(slice) + fmt.Println(len(result)) + // Output: + // 3 +} diff --git a/utils/collection/sort_test.go b/utils/collection/sort_test.go new file mode 100644 index 0000000..26a0dfc --- /dev/null +++ b/utils/collection/sort_test.go @@ -0,0 +1,191 @@ +package collection_test + +import ( + "github.com/kercylan98/minotaur/utils/collection" + "sort" + "testing" +) + +func TestDescBy(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{ + {"TestDescBy_NonEmptySlice", []int{1, 2, 3}, []int{3, 2, 1}}, + {"TestDescBy_EmptySlice", []int{}, []int{}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + sort.Slice(c.input, func(i, j int) bool { + return collection.DescBy(c.input[i], c.input[j]) + }) + for i, v := range c.input { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + } + }) + } +} + +func TestAscBy(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{ + {"TestAscBy_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}}, + {"TestAscBy_EmptySlice", []int{}, []int{}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + sort.Slice(c.input, func(i, j int) bool { + return collection.AscBy(c.input[i], c.input[j]) + }) + for i, v := range c.input { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + } + }) + } +} + +func TestDesc(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{ + {"TestDesc_NonEmptySlice", []int{1, 2, 3}, []int{3, 2, 1}}, + {"TestDesc_EmptySlice", []int{}, []int{}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.Desc(&c.input, func(index int) int { + return c.input[index] + }) + for i, v := range c.input { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + } + }) + } +} + +func TestDescByClone(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{ + {"TestDescByClone_NonEmptySlice", []int{1, 2, 3}, []int{3, 2, 1}}, + {"TestDescByClone_EmptySlice", []int{}, []int{}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.DescByClone(c.input, func(index int) int { + return c.input[index] + }) + for i, v := range result { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, result) + } + } + }) + } +} + +func TestAsc(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{ + {"TestAsc_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}}, + {"TestAsc_EmptySlice", []int{}, []int{}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.Asc(&c.input, func(index int) int { + return c.input[index] + }) + for i, v := range c.input { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + } + }) + } +} + +func TestAscByClone(t *testing.T) { + var cases = []struct { + name string + input []int + expected []int + }{ + {"TestAscByClone_NonEmptySlice", []int{1, 2, 3}, []int{1, 2, 3}}, + {"TestAscByClone_EmptySlice", []int{}, []int{}}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.AscByClone(c.input, func(index int) int { + return c.input[index] + }) + for i, v := range result { + if v != c.expected[i] { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, result) + } + } + }) + } +} + +func TestShuffle(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{ + {"TestShuffle_NonEmptySlice", []int{1, 2, 3}, 3}, + {"TestShuffle_EmptySlice", []int{}, 0}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + collection.Shuffle(&c.input) + if len(c.input) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, c.input) + } + }) + } +} + +func TestShuffleByClone(t *testing.T) { + var cases = []struct { + name string + input []int + expected int + }{ + {"TestShuffleByClone_NonEmptySlice", []int{1, 2, 3}, 3}, + {"TestShuffleByClone_EmptySlice", []int{}, 0}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := collection.ShuffleByClone(c.input) + if len(result) != c.expected { + t.Fatalf("%s failed, expected: %v, actual: %v", c.name, c.expected, result) + } + }) + } +}