diff --git a/first.go b/first.go new file mode 100644 index 0000000..b9a9348 --- /dev/null +++ b/first.go @@ -0,0 +1,33 @@ +package underscore + +import "errors" + +// ErrEmptySlice is returned when trying to get the first element of an empty slice +var ErrEmptySlice = errors.New("underscore: empty slice") + +// First returns the first element of the slice. +// Returns an error if the slice is empty. +func First[T any](values []T) (T, error) { + var zero T + if len(values) == 0 { + return zero, ErrEmptySlice + } + return values[0], nil +} + +// FirstN returns the first n elements of the slice. +// If n is greater than the slice length, returns the entire slice. +// If n is less than or equal to 0, returns an empty slice. +func FirstN[T any](values []T, n int) []T { + if n <= 0 { + return []T{} + } + if n >= len(values) { + res := make([]T, len(values)) + copy(res, values) + return res + } + res := make([]T, n) + copy(res, values[:n]) + return res +} diff --git a/first_test.go b/first_test.go new file mode 100644 index 0000000..8560e59 --- /dev/null +++ b/first_test.go @@ -0,0 +1,97 @@ +package underscore_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + u "github.com/rjNemo/underscore" +) + +func TestFirst(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + result, err := u.First(nums) + assert.NoError(t, err) + assert.Equal(t, 1, result) +} + +func TestFirstEmpty(t *testing.T) { + _, err := u.First([]int{}) + assert.Error(t, err) + assert.True(t, errors.Is(err, u.ErrEmptySlice)) +} + +func TestFirstSingleElement(t *testing.T) { + result, err := u.First([]int{42}) + assert.NoError(t, err) + assert.Equal(t, 42, result) +} + +func TestFirstStrings(t *testing.T) { + words := []string{"hello", "world"} + result, err := u.First(words) + assert.NoError(t, err) + assert.Equal(t, "hello", result) +} + +func TestFirstN(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + result := u.FirstN(nums, 3) + assert.Equal(t, []int{1, 2, 3}, result) +} + +func TestFirstNEmpty(t *testing.T) { + result := u.FirstN([]int{}, 3) + assert.Equal(t, []int{}, result) +} + +func TestFirstNZero(t *testing.T) { + nums := []int{1, 2, 3} + result := u.FirstN(nums, 0) + assert.Equal(t, []int{}, result) +} + +func TestFirstNNegative(t *testing.T) { + nums := []int{1, 2, 3} + result := u.FirstN(nums, -5) + assert.Equal(t, []int{}, result) +} + +func TestFirstNGreaterThanLength(t *testing.T) { + nums := []int{1, 2, 3} + result := u.FirstN(nums, 10) + assert.Equal(t, []int{1, 2, 3}, result) +} + +func TestFirstNSingleElement(t *testing.T) { + result := u.FirstN([]int{42}, 1) + assert.Equal(t, []int{42}, result) +} + +func TestFirstNAll(t *testing.T) { + nums := []int{1, 2, 3} + result := u.FirstN(nums, 3) + assert.Equal(t, []int{1, 2, 3}, result) +} + +func BenchmarkFirst(b *testing.B) { + nums := []int{1, 2, 3, 4, 5} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + u.First(nums) + } +} + +func BenchmarkFirstN(b *testing.B) { + nums := make([]int, 1000) + for i := range nums { + nums[i] = i + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + u.FirstN(nums, 100) + } +}