Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: experimental error handling for Map #11

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions pkg/arrays/arrays.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ func Map[T any, U any](xs []T, f types.Mapper[T, U]) []U {
return mapped
}

// Map creates a new array of values by passing each element
// of xs to the MapperWithError function f. This function short
// circuits if f returns an error, returning the elements so far
// NOTE: The output array may be of the same size as the input
// array, xs, but may not necessarily be fully populated
func MapWithError[T any, U any](xs []T, f types.MapperWithError[T, U]) ([]U, error) {
mapped := make([]U, len(xs))

for i, x := range xs {
res, err := f(x)
if err != nil {
return mapped, err
} else {
mapped[i] = res
}
}

return mapped, nil
}

// Reduce produces a value from xs by accumulating
// the result of each element as passed through the Reducer
// function f. The first element is passed to the Reducer with
Expand Down Expand Up @@ -46,7 +66,7 @@ func Filter[T any](xs []T, f types.Predicate[T]) []T {
func Take[T any](xs []T, n int) []T {
taken := make([]T, n)

for i := 0; i<n; i++ {
for i := 0; i < n; i++ {
taken[i] = xs[i]
}

Expand All @@ -56,7 +76,7 @@ func Take[T any](xs []T, n int) []T {
// TakeWhile returns a new array containing the first elements
// of xs which pass the supplied Predicate f. Once the Predicate
// returns false that element and all subsequent elements are
// discarded.
// discarded.
func TakeWhile[T any](xs []T, f types.Predicate[T]) []T {
taken := []T{}

Expand All @@ -75,7 +95,7 @@ func TakeWhile[T any](xs []T, f types.Predicate[T]) []T {
func Drop[T any](xs []T, n int) []T {
taken := make([]T, len(xs)-n)

for i := n; i<len(xs); i++ {
for i := n; i < len(xs); i++ {
taken[i-n] = xs[i]
}

Expand Down Expand Up @@ -193,7 +213,7 @@ func setFromArray[T comparable](xs []T) map[T]bool {
// stop converting to maps to achieve this
xsSet := map[T]bool{}
for _, x := range xs {
xsSet[x] = true
xsSet[x] = true
}
return xsSet
}
Expand Down Expand Up @@ -271,7 +291,7 @@ func Chunk[T any](xs []T, chunkSize int) [][]T {
result := [][]T{}

// Full chunks
for i := 0; i < len(xs) / chunkSize; i++ {
for i := 0; i < len(xs)/chunkSize; i++ {
newChunk := []T{}
result = append(result, []T{})
for j := 0; j < chunkSize; j++ {
Expand All @@ -282,7 +302,7 @@ func Chunk[T any](xs []T, chunkSize int) [][]T {

// Remaining chunk
remaining := len(xs) % chunkSize
if len(xs) % chunkSize > 0 {
if len(xs)%chunkSize > 0 {
newChunk := []T{}

for i := 0; i < remaining; i++ {
Expand Down Expand Up @@ -320,7 +340,6 @@ func Without[T any](xs []T, f types.Predicate[T]) []T {
return taken
}


// Every returns true if all elements of the supplied array xs
// pass the Predicate f. An empty array yields true.
func Every[T any](xs []T, f types.Predicate[T]) bool {
Expand Down
43 changes: 43 additions & 0 deletions pkg/arrays/arrays_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package arrays

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -14,6 +15,48 @@ func TestMap(t *testing.T) {
assert.Equal(t, []int{11, 12, 13}, result)
}

func TestMap_ErrorReturns(t *testing.T) {
result := Map([]int{1, 2, 3}, func(x int) struct {
int
error
} {
if x > 2 {
return struct {
int
error
}{0, errors.New("Failed to map a value")}
} else {
return struct {
int
error
}{x + 10, nil}
}
})

assert.Equal(t, []struct {
int
error
}{{11, nil}, {12, nil}, {0, errors.New("Failed to map a value")}}, result)
}

func TestMapWithError(t *testing.T) {
result, err := MapWithError([]int{1, 2, 3}, func(x int) (int, error) {
return x + 10, nil
})

assert.Equal(t, []int{11, 12, 13}, result)
assert.Nil(t, err)
}

func TestMapWithError_ReturnsError(t *testing.T) {
result, err := MapWithError([]int{1, 2, 3}, func(x int) (int, error) {
return x + 10, errors.New("test error")
})

assert.Equal(t, []int{0, 0, 0}, result)
assert.NotNil(t, err)
}

func TestReduce(t *testing.T) {
// Sum reduce
result := Reduce([]int{1, 2, 3}, 0, func(x int, a int) int {
Expand Down
10 changes: 8 additions & 2 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ package types
// Mapper is a function which accepts one type
// and returns another.
type Mapper[T any, U any] func(T) U

// Mapper is a function which accepts one type
// and returns another.
type MapperWithError[T any, U any] func(T) (U, error)

// Predicate is a function which accepts one type
// and returns a bool indicating whether the predicate
// was satisfied.
type Predicate[T any] func(T) bool

// Reducer is a function which accepts a parameter of
// type T and a parameter of type U, the accumulator
// and returns a new value of type U (this return value
// is passed with the next element in the Reduce
// function).
type Reducer[T any, U any] func(T, U) U
// function).
type Reducer[T any, U any] func(T, U) U