diff --git a/chunk.go b/chunk.go new file mode 100644 index 0000000..0849ec8 --- /dev/null +++ b/chunk.go @@ -0,0 +1,26 @@ +package util + +// ChunkStrings splits input into consecutive slices of at most size elements. +// The last slice holds whatever is left over and may be shorter than the +// others. Each returned slice is a copy, so appending to one of them cannot +// disturb another. A size below one, or an empty input, returns nil. +func ChunkStrings(input []string, size int) [][]string { + if size < 1 || len(input) == 0 { + return nil + } + + out := make([][]string, 0, (len(input)+size-1)/size) + + for start := 0; start < len(input); start += size { + end := start + size + if end > len(input) { + end = len(input) + } + + chunk := make([]string, end-start) + copy(chunk, input[start:end]) + out = append(out, chunk) + } + + return out +} diff --git a/chunk_test.go b/chunk_test.go new file mode 100644 index 0000000..e47199d --- /dev/null +++ b/chunk_test.go @@ -0,0 +1,43 @@ +package util + +import ( + "reflect" + "testing" +) + +func TestChunkStrings(t *testing.T) { + tests := []struct { + name string + input []string + size int + expected [][]string + }{ + {"exact multiple", []string{"a", "b", "c", "d"}, 2, [][]string{{"a", "b"}, {"c", "d"}}}, + {"with a remainder", []string{"a", "b", "c"}, 2, [][]string{{"a", "b"}, {"c"}}}, + {"size larger than input", []string{"a", "b"}, 5, [][]string{{"a", "b"}}}, + {"size of one", []string{"a", "b"}, 1, [][]string{{"a"}, {"b"}}}, + {"size of zero", []string{"a", "b"}, 0, nil}, + {"negative size", []string{"a", "b"}, -1, nil}, + {"empty input", []string{}, 2, nil}, + {"nil input", nil, 2, nil}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := ChunkStrings(test.input, test.size) + if !reflect.DeepEqual(got, test.expected) { + t.Errorf("expected %#v got %#v", test.expected, got) + } + }) + } +} + +func TestChunkStringsReturnsCopies(t *testing.T) { + input := []string{"a", "b", "c", "d"} + chunks := ChunkStrings(input, 2) + chunks[0][0] = "changed" + + if input[0] != "a" { + t.Errorf("expected the input to be unchanged, got %#v", input) + } +}