From 9dba7ffe19f0b5502e06d3cafcd1602736e6648e Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Thu, 18 Jan 2024 17:16:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20collection=20=E5=8C=85=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20ConvertSliceToBatches=E3=80=81ConvertMapKeysToBatch?= =?UTF-8?q?es=E3=80=81ConvertMapValuesToBatches=20=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E7=94=A8=E4=BA=8E=E5=B0=86=E5=88=87=E7=89=87=E6=88=96?= =?UTF-8?q?=20map=20=E8=BD=AC=E6=8D=A2=E4=B8=BA=E6=8C=89=E7=89=B9=E5=AE=9A?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E5=88=86=E6=89=B9=E7=9A=84=E6=89=B9=E6=AC=A1?= =?UTF-8?q?=E5=88=87=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/collection/convert.go | 50 ++++++++++++++ utils/collection/convert_example_test.go | 26 ++++++++ utils/collection/convert_test.go | 84 ++++++++++++++++++++++++ 3 files changed, 160 insertions(+) diff --git a/utils/collection/convert.go b/utils/collection/convert.go index 63df650..e5e7f54 100644 --- a/utils/collection/convert.go +++ b/utils/collection/convert.go @@ -1,5 +1,55 @@ package collection +// ConvertSliceToBatches 将切片 s 转换为分批次的切片,当 batchSize 小于等于 0 或者 s 长度为 0 时,将会返回 nil +func ConvertSliceToBatches[S ~[]V, V any](s S, batchSize int) []S { + if len(s) == 0 || batchSize <= 0 { + return nil + } + var batches = make([]S, 0, len(s)/batchSize+1) + for i := 0; i < len(s); i += batchSize { + var end = i + batchSize + if end > len(s) { + end = len(s) + } + batches = append(batches, s[i:end]) + } + return batches +} + +// ConvertMapKeysToBatches 将映射的键转换为分批次的切片,当 batchSize 小于等于 0 或者 m 长度为 0 时,将会返回 nil +func ConvertMapKeysToBatches[M ~map[K]V, K comparable, V any](m M, batchSize int) [][]K { + if len(m) == 0 || batchSize <= 0 { + return nil + } + var batches = make([][]K, 0, len(m)/batchSize+1) + var keys = ConvertMapKeysToSlice(m) + for i := 0; i < len(keys); i += batchSize { + var end = i + batchSize + if end > len(keys) { + end = len(keys) + } + batches = append(batches, keys[i:end]) + } + return batches +} + +// ConvertMapValuesToBatches 将映射的值转换为分批次的切片,当 batchSize 小于等于 0 或者 m 长度为 0 时,将会返回 nil +func ConvertMapValuesToBatches[M ~map[K]V, K comparable, V any](m M, batchSize int) [][]V { + if len(m) == 0 || batchSize <= 0 { + return nil + } + var batches = make([][]V, 0, len(m)/batchSize+1) + var values = ConvertMapValuesToSlice(m) + for i := 0; i < len(values); i += batchSize { + var end = i + batchSize + if end > len(values) { + end = len(values) + } + batches = append(batches, values[i:end]) + } + return batches +} + // ConvertSliceToAny 将切片转换为任意类型的切片 func ConvertSliceToAny[S ~[]V, V any](s S) []any { if len(s) == 0 { diff --git a/utils/collection/convert_example_test.go b/utils/collection/convert_example_test.go index 3658224..468e6ea 100644 --- a/utils/collection/convert_example_test.go +++ b/utils/collection/convert_example_test.go @@ -4,8 +4,33 @@ import ( "fmt" "github.com/kercylan98/minotaur/utils/collection" "reflect" + "sort" ) +func ExampleConvertSliceToBatches() { + result := collection.ConvertSliceToBatches([]int{1, 2, 3}, 2) + for _, v := range result { + fmt.Println(v) + } + // Output: + // [1 2] + // [3] +} + +func ExampleConvertMapKeysToBatches() { + result := collection.ConvertMapKeysToBatches(map[int]int{1: 1, 2: 2, 3: 3}, 2) + fmt.Println(len(result)) + // Output: + // 2 +} + +func ExampleConvertMapValuesToBatches() { + result := collection.ConvertMapValuesToBatches(map[int]int{1: 1, 2: 2, 3: 3}, 2) + fmt.Println(len(result)) + // Output: + // 2 +} + func ExampleConvertSliceToAny() { result := collection.ConvertSliceToAny([]int{1, 2, 3}) fmt.Println(reflect.TypeOf(result).String(), len(result)) @@ -60,6 +85,7 @@ func ExampleConvertSliceToBoolMap() { func ExampleConvertMapKeysToSlice() { result := collection.ConvertMapKeysToSlice(map[int]int{1: 1, 2: 2, 3: 3}) + sort.Ints(result) for i, v := range result { fmt.Println(i, v) } diff --git a/utils/collection/convert_test.go b/utils/collection/convert_test.go index 8b875eb..a117cc9 100644 --- a/utils/collection/convert_test.go +++ b/utils/collection/convert_test.go @@ -6,6 +6,90 @@ import ( "testing" ) +func TestConvertSliceToBatches(t *testing.T) { + var cases = []struct { + name string + input []int + batch int + expected [][]int + }{ + {name: "TestConvertSliceToBatches_NonEmpty", input: []int{1, 2, 3}, batch: 2, expected: [][]int{{1, 2}, {3}}}, + {name: "TestConvertSliceToBatches_Empty", input: []int{}, batch: 2, expected: nil}, + {name: "TestConvertSliceToBatches_Nil", input: nil, batch: 2, expected: nil}, + {name: "TestConvertSliceToBatches_NonPositive", input: []int{1, 2, 3}, batch: 0, expected: nil}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertSliceToBatches(c.input, c.batch) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for i := 0; i < len(actual); i++ { + av, ev := actual[i], c.expected[i] + if len(av) != len(ev) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + for j := 0; j < len(av); j++ { + aj, ej := av[j], ev[j] + if reflect.TypeOf(aj).Kind() != reflect.TypeOf(ej).Kind() { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + if aj != ej { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + } + } + }) + } +} + +func TestConvertMapKeysToBatches(t *testing.T) { + var cases = []struct { + name string + input map[int]int + batch int + expected [][]int + }{ + {name: "TestConvertMapKeysToBatches_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, batch: 2, expected: [][]int{{1, 2}, {3}}}, + {name: "TestConvertMapKeysToBatches_Empty", input: map[int]int{}, batch: 2, expected: nil}, + {name: "TestConvertMapKeysToBatches_Nil", input: nil, batch: 2, expected: nil}, + {name: "TestConvertMapKeysToBatches_NonPositive", input: map[int]int{1: 1, 2: 2, 3: 3}, batch: 0, expected: nil}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertMapKeysToBatches(c.input, c.batch) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + }) + } +} + +func TestConvertMapValuesToBatches(t *testing.T) { + var cases = []struct { + name string + input map[int]int + batch int + expected [][]int + }{ + {name: "TestConvertMapValuesToBatches_NonEmpty", input: map[int]int{1: 1, 2: 2, 3: 3}, batch: 2, expected: [][]int{{1, 2}, {3}}}, + {name: "TestConvertMapValuesToBatches_Empty", input: map[int]int{}, batch: 2, expected: nil}, + {name: "TestConvertMapValuesToBatches_Nil", input: nil, batch: 2, expected: nil}, + {name: "TestConvertMapValuesToBatches_NonPositive", input: map[int]int{1: 1, 2: 2, 3: 3}, batch: 0, expected: nil}, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := collection.ConvertMapValuesToBatches(c.input, c.batch) + if len(actual) != len(c.expected) { + t.Errorf("expected: %v, actual: %v", c.expected, actual) + } + }) + } +} + func TestConvertSliceToAny(t *testing.T) { var cases = []struct { name string