From f51726a9009d38cf5032b7874f404d5747b78ad5 Mon Sep 17 00:00:00 2001 From: xuzhiwei Date: Thu, 10 Mar 2022 17:13:37 +0800 Subject: [PATCH 1/3] Add Range --- README.md | 27 ++++++++++++++++++++++++ util.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ util_test.go | 40 ++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 util.go create mode 100644 util_test.go diff --git a/README.md b/README.md index dd3cc2d9..f0d9cadb 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ Other functional programming helpers: - ToPtr - ToSlicePtr - Attempt +- Range / RangeFrom / RangeOpen Constraints: @@ -794,6 +795,32 @@ iter, err := lo.Attempt(0, func(i int) error { // nil ``` +### Range / RangeFrom / RangeOpen +Creates an array of numbers (positive and/or negative) progressing from start up to, but not including end. +```go +result := Range(4) +// [0, 1, 2, 3] + +result := Range(-4); +// [0, -1, -2, -3] + +result := RangeFrom(1, 5); +// [1, 2, 3, 4] + +result := RangeOpen(0, 20, 5); +// [0, 5, 10, 15] + +result := RangeOpen(0, -4, -1); +// [0, -1, -2, -3] + +// default step: 1 +result := RangeOpen(1, 4, 0); +// [1, 2, 3] + +result := Range(0); +// [] +``` + For more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff). ## 🛩 Benchmark diff --git a/util.go b/util.go new file mode 100644 index 00000000..f0e2ce1e --- /dev/null +++ b/util.go @@ -0,0 +1,58 @@ +package lo + +// Range creates an array of numbers (positive and/or negative) with given length. +func Range(elementNum int) []int { + if elementNum >= 0 { + result := make([]int, elementNum) + for i := 0; i < elementNum; i++ { + result[i] = i + } + return result + } + result := make([]int, -elementNum) + for i := 0; i < -elementNum; i++ { + result[i] = -i + } + return result +} + +// RangeFrom creates an array of numbers from start with specified length. +func RangeFrom(start, elementNum int) []int { + if elementNum >= 0 { + result := make([]int, elementNum) + for i := 0; i < elementNum; i++ { + result[i] = i + start + } + return result + } + result := make([]int, -elementNum) + for i := 0; i < -elementNum; i++ { + result[i] = start - i + } + return result +} + +// RangeOpen creates an array of numbers (positive and/or negative) progressing from start up to, but not including end. +// step is default 1 when set to zero or can not reach end. +func RangeOpen(start, end, step int) []int { + var result []int + if start == end { + return result + } + if start < end { + if step <= 0 { + step = 1 + } + for i := start; i < end; i += step { + result = append(result, i) + } + return result + } + if step >= 0 { + step = -1 + } + for i := start; i > end; i += step { + result = append(result, i) + } + return result +} diff --git a/util_test.go b/util_test.go new file mode 100644 index 00000000..cf2206df --- /dev/null +++ b/util_test.go @@ -0,0 +1,40 @@ +package lo + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestRange(t *testing.T) { + is := assert.New(t) + result1 := Range(4) + result2 := Range(-4) + result3 := Range(0) + is.Equal(result1, []int{0, 1, 2, 3}) + is.Equal(result2, []int{0, -1, -2, -3}) + is.Equal(result3, []int{}) +} + +func TestRangeFrom(t *testing.T) { + is := assert.New(t) + result1 := RangeFrom(1, 5) + result2 := RangeFrom(-1, -5) + result3 := RangeFrom(10, 0) + is.Equal(result1, []int{1, 2, 3, 4, 5}) + is.Equal(result2, []int{-1, -2, -3, -4, -5}) + is.Equal(result3, []int{}) +} + +func TestRangeClose(t *testing.T) { + is := assert.New(t) + result1 := RangeOpen(0, 20, 6) + result2 := RangeOpen(0, 3, -5) + result3 := RangeOpen(0, -4, -1) + result4 := RangeOpen(1, 4, 0) + result5 := RangeOpen(1, 1, 0) + is.Equal(result1, []int{0, 6, 12, 18}) + is.Equal(result2, []int{0, 1, 2}) + is.Equal(result3, []int{0, -1, -2, -3}) + is.Equal(result4, []int{1, 2, 3}) + is.Equal(len(result5), 0) +} From 599e76f8e637a7d8a5a1108987f2497962eb3f48 Mon Sep 17 00:00:00 2001 From: xuzhiwei Date: Thu, 10 Mar 2022 20:07:57 +0800 Subject: [PATCH 2/3] Modify Range --- README.md | 18 ++++++++++-------- util.go | 30 ++++++++++++++++-------------- util_test.go | 22 +++++++++++++--------- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index f0d9cadb..ef5739d3 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Other functional programming helpers: - ToPtr - ToSlicePtr - Attempt -- Range / RangeFrom / RangeOpen +- Range / RangeFrom / RangeWithSteps Constraints: @@ -795,7 +795,7 @@ iter, err := lo.Attempt(0, func(i int) error { // nil ``` -### Range / RangeFrom / RangeOpen +### Range / RangeFrom / RangeWithSteps Creates an array of numbers (positive and/or negative) progressing from start up to, but not including end. ```go result := Range(4) @@ -806,16 +806,18 @@ result := Range(-4); result := RangeFrom(1, 5); // [1, 2, 3, 4] + +result := RangeFrom[float64](1.0, 5); +// [1.0, 2.0, 3.0, 4.0] -result := RangeOpen(0, 20, 5); +result := RangeWithSteps(0, 20, 5); // [0, 5, 10, 15] -result := RangeOpen(0, -4, -1); -// [0, -1, -2, -3] +result := RangeWithSteps[float32](-1.0, -4.0, -1.0); +// [-1.0, -2.0, -3.0] -// default step: 1 -result := RangeOpen(1, 4, 0); -// [1, 2, 3] +result := RangeWithSteps(1, 4, -1); +// [] result := Range(0); // [] diff --git a/util.go b/util.go index f0e2ce1e..000466e7 100644 --- a/util.go +++ b/util.go @@ -1,5 +1,7 @@ package lo +import "golang.org/x/exp/constraints" + // Range creates an array of numbers (positive and/or negative) with given length. func Range(elementNum int) []int { if elementNum >= 0 { @@ -17,39 +19,39 @@ func Range(elementNum int) []int { } // RangeFrom creates an array of numbers from start with specified length. -func RangeFrom(start, elementNum int) []int { +func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) []T { if elementNum >= 0 { - result := make([]int, elementNum) + result := make([]T, elementNum) for i := 0; i < elementNum; i++ { - result[i] = i + start + result[i] = T(i) + start } return result } - result := make([]int, -elementNum) + result := make([]T, -elementNum) for i := 0; i < -elementNum; i++ { - result[i] = start - i + result[i] = start - T(i) } return result } -// RangeOpen creates an array of numbers (positive and/or negative) progressing from start up to, but not including end. -// step is default 1 when set to zero or can not reach end. -func RangeOpen(start, end, step int) []int { - var result []int - if start == end { +// RangeWithSteps creates an array of numbers (positive and/or negative) progressing from start up to, but not including end. +// step set to zero will return empty array. +func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) []T { + result := []T{} + if start == end || step == 0 { return result } if start < end { - if step <= 0 { - step = 1 + if step < 0 { + return result } for i := start; i < end; i += step { result = append(result, i) } return result } - if step >= 0 { - step = -1 + if step > 0 { + return result } for i := start; i > end; i += step { result = append(result, i) diff --git a/util_test.go b/util_test.go index cf2206df..f1ed4ff3 100644 --- a/util_test.go +++ b/util_test.go @@ -20,21 +20,25 @@ func TestRangeFrom(t *testing.T) { result1 := RangeFrom(1, 5) result2 := RangeFrom(-1, -5) result3 := RangeFrom(10, 0) + result4 := RangeFrom[float64](2.0, 3) + result5 := RangeFrom[float64](-2.0, -3) is.Equal(result1, []int{1, 2, 3, 4, 5}) is.Equal(result2, []int{-1, -2, -3, -4, -5}) is.Equal(result3, []int{}) + is.Equal(result4, []float64{2.0, 3.0, 4.0}) + is.Equal(result5, []float64{-2.0, -3.0, -4.0}) } func TestRangeClose(t *testing.T) { is := assert.New(t) - result1 := RangeOpen(0, 20, 6) - result2 := RangeOpen(0, 3, -5) - result3 := RangeOpen(0, -4, -1) - result4 := RangeOpen(1, 4, 0) - result5 := RangeOpen(1, 1, 0) + result1 := RangeWithSteps(0, 20, 6) + result2 := RangeWithSteps(0, 3, -5) + result3 := RangeWithSteps(1, 1, 0) + result4 := RangeWithSteps[float64](1.0, 4.0, 2.0) + result5 := RangeWithSteps[float32](-1.0, -4.0, -1.0) is.Equal(result1, []int{0, 6, 12, 18}) - is.Equal(result2, []int{0, 1, 2}) - is.Equal(result3, []int{0, -1, -2, -3}) - is.Equal(result4, []int{1, 2, 3}) - is.Equal(len(result5), 0) + is.Equal(result2, []int{}) + is.Equal(result3, []int{}) + is.Equal(result4, []float64{1.0, 3.0}) + is.Equal(result5, []float32{-1.0, -2.0, -3.0}) } From f30ae9731967e20a9b888776077b522492d33923 Mon Sep 17 00:00:00 2001 From: xuzhiwei Date: Thu, 10 Mar 2022 22:47:02 +0800 Subject: [PATCH 3/3] Modify Range --- util.go | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/util.go b/util.go index 000466e7..41ca9c80 100644 --- a/util.go +++ b/util.go @@ -4,32 +4,22 @@ import "golang.org/x/exp/constraints" // Range creates an array of numbers (positive and/or negative) with given length. func Range(elementNum int) []int { - if elementNum >= 0 { - result := make([]int, elementNum) - for i := 0; i < elementNum; i++ { - result[i] = i - } - return result - } - result := make([]int, -elementNum) - for i := 0; i < -elementNum; i++ { - result[i] = -i + length := If(elementNum < 0, -elementNum).Else(elementNum) + result := make([]int, length) + step := If(elementNum < 0, -1).Else(1) + for i, j := 0, 0; i < length; i, j = i+1, j+step { + result[i] = j } return result } // RangeFrom creates an array of numbers from start with specified length. func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) []T { - if elementNum >= 0 { - result := make([]T, elementNum) - for i := 0; i < elementNum; i++ { - result[i] = T(i) + start - } - return result - } - result := make([]T, -elementNum) - for i := 0; i < -elementNum; i++ { - result[i] = start - T(i) + length := If(elementNum < 0, -elementNum).Else(elementNum) + result := make([]T, length) + step := If(elementNum < 0, -1).Else(1) + for i, j := 0, start; i < length; i, j = i+1, j+T(step) { + result[i] = j } return result }