From 63d4339ad34787ab5d31b2cde9067b4a32425e5f Mon Sep 17 00:00:00 2001 From: Sergey Dobrodey Date: Thu, 23 Jan 2025 13:44:12 +0200 Subject: [PATCH] transform: add ChunkBy --- README.md | 1 + transform.go | 20 +++++++++++++++ transform_test.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/README.md b/README.md index be03470..ddb2a21 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ This package provides several functions for working with collections: * `AsyncTryTransformBy[T, K any](parent context.Context, source []T, transform func(context.Context, T) (K, error)) ([]K, error)` * `ChannelsMerge[T any](args ...<-chan T) <-chan T` * `ChannelsReadonly[T any](args ...chan T) []<-chan T` +* `ChunkBy[T any](source []T, size int) [][]T` * `Contains[T comparable](source []T, item T) bool` * `Copy[T any](source []T) []T` * `Difference[T comparable](a []T, b []T) []T` diff --git a/transform.go b/transform.go index 9bb2a11..02a5643 100644 --- a/transform.go +++ b/transform.go @@ -113,6 +113,26 @@ func Duplicates[T comparable](source []T) []T { return Distinct(result) } +// ChunkBy divides a slice of Type T into smaller chunks of the specified size. +func ChunkBy[T any](source []T, size int) [][]T { + if size <= 0 || len(source) == 0 { + return nil + } + + var chunks = make([][]T, 0, len(source)/size) + + for i := 0; i < len(source); i += size { + var end = i + size + if end > len(source) { + end = len(source) + } + + chunks = append(chunks, source[i:end]) + } + + return chunks +} + // AsyncTransformBy async transform the source slice of type T to a new slice of type K using the provided transform function. func AsyncTransformBy[T, K any](source []T, transform func(T) K) []K { var results = make([]K, len(source)) diff --git a/transform_test.go b/transform_test.go index 385b9a2..9851997 100644 --- a/transform_test.go +++ b/transform_test.go @@ -447,3 +447,65 @@ func TestAsyncTransformBy(t *testing.T) { }) } } + +func TestChunkBy(t *testing.T) { + cases := []struct { + name string + source []int + size int + want [][]int + }{ + { + name: "evenly divisible chunks", + source: []int{1, 2, 3, 4, 5, 6}, + size: 2, + want: [][]int{{1, 2}, {3, 4}, {5, 6}}, + }, + { + name: "remainder chunk", + source: []int{1, 2, 3, 4, 5}, + size: 2, + want: [][]int{{1, 2}, {3, 4}, {5}}, + }, + { + name: "chunk size larger than source", + source: []int{1, 2, 3}, + size: 5, + want: [][]int{{1, 2, 3}}, + }, + { + name: "chunk size equal to source length", + source: []int{1, 2, 3}, + size: 3, + want: [][]int{{1, 2, 3}}, + }, + { + name: "empty source slice", + source: []int{}, + size: 3, + want: nil, + }, + { + name: "zero chunk size", + source: []int{1, 2, 3}, + size: 0, + want: nil, + }, + { + name: "negative chunk size", + source: []int{1, 2, 3}, + size: -1, + want: nil, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := collection.ChunkBy(tc.source, tc.size) + + if !slices.EqualFunc(got, tc.want, slices.Equal[[]int]) { + t.Errorf("ChunkBy(%v, %d) = %v; want %v", tc.source, tc.size, got, tc.want) + } + }) + } +}