diff --git a/slices/slices.go b/slices/slices.go index 5e8158bba..4ad0409d9 100644 --- a/slices/slices.go +++ b/slices/slices.go @@ -404,6 +404,23 @@ func Clip[S ~[]E, E any](s S) S { return s[:len(s):len(s)] } + +// ChunkBy takes values from s and and appends them to chunks +// the appended values will have len <= n +// if n is 0 default chunk size will be 1 +// if len s == 0 base type [][]T is returned with len == 0 +func ChunkBy[T any](s []T, n int) (chunks [][]T) { + if n == 0 { + n = 1 + } + if len(s) == 0 { + return [][]T{} + } + for n < len(s) { + s, chunks = s[n:], append(chunks, s[0:n:n]) + } + return append(chunks, s) +} // Rotation algorithm explanation: // // rotate left by 2 @@ -496,4 +513,5 @@ func Reverse[S ~[]E, E any](s S) { for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } + } diff --git a/slices/slices_test.go b/slices/slices_test.go index 7371bdb88..487c528e3 100644 --- a/slices/slices_test.go +++ b/slices/slices_test.go @@ -6,6 +6,7 @@ package slices import ( "math" + "reflect" "strings" "testing" ) @@ -998,6 +999,155 @@ func BenchmarkReplace(b *testing.B) { } + +func TestChunkBy(t *testing.T) { + + type args struct { + s []string + n int + } + tests := []struct { + name string + args args + wantChunks [][]string + }{ + { + name: "0 len slice 0 n", + args: args{ + s: []string{}, + n: 0, + }, + wantChunks: [][]string{}, + }, + { + name: "1 len slice 0 n", + args: args{ + s: []string{}, + n: 0, + }, + wantChunks: [][]string{}, + }, + { + name: "2 len slice 0 n", + args: args{ + s: []string{"", ""}, + n: 0, + }, + wantChunks: [][]string{{""}, {""}}, + }, + { + name: "3 len slice 0 n", + args: args{ + s: []string{"", "", ""}, + n: 0, + }, + wantChunks: [][]string{{""}, {""}, {""}}, + }, + { + name: "0 len slice 1 n", + args: args{ + s: []string{}, + n: 1, + }, + wantChunks: [][]string{}, + }, + { + name: "1 len slice 1 n", + args: args{ + s: []string{""}, + n: 1, + }, + wantChunks: [][]string{{""}}, + }, + { + name: "2 len slice 1 n", + args: args{ + s: []string{"", ""}, + n: 1, + }, + wantChunks: [][]string{{""}, {""}}, + }, + { + name: "3 len slice 1 n", + args: args{ + s: []string{"", "", ""}, + n: 1, + }, + wantChunks: [][]string{{""}, {""}, {""}}, + }, + { + name: "0 len slice 2 n", + args: args{ + s: []string{}, + n: 2, + }, + wantChunks: [][]string{}, + }, + { + name: "1 len slice 2 n", + args: args{ + s: []string{""}, + n: 2, + }, + wantChunks: [][]string{{""}}, + }, + { + name: "2 len slice 2 n", + args: args{ + s: []string{"", ""}, + n: 2, + }, + wantChunks: [][]string{{"", ""}}, + }, + { + name: "3 len slice 2 n", + args: args{ + s: []string{"", "", ""}, + n: 2, + }, + wantChunks: [][]string{{"", ""}, {""}}, + }, + { + name: "0 len slice 3 n", + args: args{ + s: []string{}, + n: 3, + }, + wantChunks: [][]string{}, + }, + { + name: "1 len slice 3 n", + args: args{ + s: []string{""}, + n: 3, + }, + wantChunks: [][]string{{""}}, + }, + { + name: "2 len slice 3 n", + args: args{ + s: []string{"", ""}, + n: 3, + }, + wantChunks: [][]string{{"", ""}}, + }, + { + name: "3 len slice 3 n", + args: args{ + s: []string{"", "", ""}, + n: 3, + }, + wantChunks: [][]string{{"", "", ""}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotChunks := ChunkBy(tt.args.s, tt.args.n); !reflect.DeepEqual(gotChunks, tt.wantChunks) { + t.Errorf("ChunkBy() = %v, want %v", gotChunks, tt.wantChunks) + } + }) + + } func TestRotate(t *testing.T) { const N = 10 s := make([]int, 0, N) @@ -1050,5 +1200,6 @@ func TestReplaceGrowthRate(t *testing.T) { want := int(math.Log(N) / math.Log(1.25)) // 1.25 == growth rate for large slices if nGrow > want { t.Errorf("too many grows. got:%d want:%d", nGrow, want) + } }