From 8a27a371d788e49145bb927401d6117a90f276ad Mon Sep 17 00:00:00 2001 From: Sylvain Prost Date: Thu, 3 Oct 2024 18:13:45 +0200 Subject: [PATCH 1/3] slices: add reduce functions --- go.mod | 5 ++-- go.sum | 4 +++ slices/reduce.go | 25 +++++++++++++++++++ slices/reduce_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 slices/reduce.go create mode 100644 slices/reduce_test.go diff --git a/go.mod b/go.mod index 67fb391..9775db1 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/upfluence/pkg -go 1.21 +go 1.23 require ( github.com/Microsoft/go-winio v0.4.14 // indirect @@ -34,7 +34,7 @@ require ( github.com/upfluence/stats v0.1.4 github.com/upfluence/thrift v2.4.4+incompatible golang.org/x/oauth2 v0.19.0 - golang.org/x/sync v0.4.0 + golang.org/x/sync v0.8.0 golang.org/x/term v0.13.0 golang.org/x/text v0.13.0 golang.org/x/time v0.3.0 @@ -57,6 +57,7 @@ require ( go.uber.org/atomic v1.6.0 // indirect go.uber.org/multierr v1.5.0 // indirect golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/sys v0.13.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 4da9e9b..e30aa0c 100644 --- a/go.sum +++ b/go.sum @@ -513,6 +513,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -560,6 +562,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -617,6 +620,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/slices/reduce.go b/slices/reduce.go new file mode 100644 index 0000000..af591da --- /dev/null +++ b/slices/reduce.go @@ -0,0 +1,25 @@ +package slices + +import "golang.org/x/exp/constraints" + +func ReduceWith[S ~[]E, E any, A any](s S, acc A, fn func(A, E) A) A { + for _, e := range s { + acc = fn(acc, e) + } + + return acc +} + +func Reduce[S ~[]E, E any, A any](s S, fn func(A, E) A) A { + var acc A + + return ReduceWith(s, acc, fn) +} + +type sumable interface { + constraints.Integer | constraints.Float | ~string +} + +func Sum[T sumable](i, j T) T { + return i + j +} diff --git a/slices/reduce_test.go b/slices/reduce_test.go new file mode 100644 index 0000000..9e72587 --- /dev/null +++ b/slices/reduce_test.go @@ -0,0 +1,57 @@ +package slices + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReduceWith(t *testing.T) { + for _, tt := range []struct { + slice []int + acc int + fn func(int, int) int + want int + }{ + {slice: []int{1, 2, 3}, acc: 0, fn: func(a, b int) int { return a + b }, want: 6}, + {slice: []int{1, 2, 3}, acc: 1, fn: func(a, b int) int { return a * b }, want: 6}, + {slice: []int{}, acc: 0, fn: func(a, b int) int { return a + b }, want: 0}, + {slice: []int{1}, acc: 0, fn: func(a, b int) int { return a + b }, want: 1}, + } { + t.Run("", func(t *testing.T) { + got := ReduceWith(tt.slice, tt.acc, tt.fn) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestReduce(t *testing.T) { + for _, tt := range []struct { + slice []int + fn func(int, int) int + want int + }{ + {slice: []int{1, 2, 3}, fn: func(a, b int) int { return a + b }, want: 6}, + {slice: []int{1, 2, 3}, fn: func(a, b int) int { return a * b }, want: 0}, + {slice: []int{}, fn: func(a, b int) int { return a + b }, want: 0}, + {slice: []int{1}, fn: func(a, b int) int { return a + b }, want: 1}, + } { + got := Reduce(tt.slice, tt.fn) + assert.Equal(t, tt.want, got) + } +} + +func TestSum(t *testing.T) { + t.Run("integer", func(t *testing.T) { + assert.Equal(t, Reduce([]int{1, 2}, Sum), 3) + assert.Equal(t, ReduceWith([]int{1, 2}, 22, Sum), 25) + }) + t.Run("float", func(t *testing.T) { + assert.Equal(t, Reduce([]float64{1.1, 2.3}, Sum), 3.4) + assert.Equal(t, ReduceWith([]float64{1.1, 2.2}, 22.22, Sum), 25.52) + }) + t.Run("string", func(t *testing.T) { + assert.Equal(t, Reduce([]string{"foo", "bar"}, Sum), "foobar") + assert.Equal(t, ReduceWith([]string{"foo", "bar"}, "buz", Sum), "buzfoobar") + }) +} From 599dc86f863efb2e303103e3cfc430f57db83b7f Mon Sep 17 00:00:00 2001 From: Sylvain Prost Date: Mon, 7 Oct 2024 11:32:33 +0200 Subject: [PATCH 2/3] slices: change `ReduceWith` to `ReduceFrom` and add comments --- go.mod | 2 +- go.sum | 10 ++++------ slices/reduce.go | 15 ++++++++++----- slices/reduce_test.go | 14 +++++++------- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 9775db1..69c6c1c 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/upfluence/log v0.0.5 github.com/upfluence/stats v0.1.4 github.com/upfluence/thrift v2.4.4+incompatible + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/oauth2 v0.19.0 golang.org/x/sync v0.8.0 golang.org/x/term v0.13.0 @@ -57,7 +58,6 @@ require ( go.uber.org/atomic v1.6.0 // indirect go.uber.org/multierr v1.5.0 // indirect golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/sys v0.13.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index e30aa0c..cda3347 100644 --- a/go.sum +++ b/go.sum @@ -178,8 +178,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -560,8 +560,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -618,9 +617,8 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/slices/reduce.go b/slices/reduce.go index af591da..8c791bf 100644 --- a/slices/reduce.go +++ b/slices/reduce.go @@ -2,18 +2,23 @@ package slices import "golang.org/x/exp/constraints" -func ReduceWith[S ~[]E, E any, A any](s S, acc A, fn func(A, E) A) A { - for _, e := range s { - acc = fn(acc, e) +// ReduceFrom will iterate over each element of slice and call reduceFn for each. +// The return value of each reduceFn is passed to the next call of reduceFn as acc, or +// in other term at each iteration on the slice. +func ReduceFrom[S ~[]E, E any, A any](slice S, acc A, reduceFn func(acc A, elem E) A) A { + for _, e := range slice { + acc = reduceFn(acc, e) } return acc } -func Reduce[S ~[]E, E any, A any](s S, fn func(A, E) A) A { +// Reduce does the same as ReduceFrom but use the zero value of A as +// the first value to be passed to reduceFn. +func Reduce[S ~[]E, E any, A any](slice S, reduceFn func(acc A, elem E) A) A { var acc A - return ReduceWith(s, acc, fn) + return ReduceFrom(slice, acc, reduceFn) } type sumable interface { diff --git a/slices/reduce_test.go b/slices/reduce_test.go index 9e72587..5ec857a 100644 --- a/slices/reduce_test.go +++ b/slices/reduce_test.go @@ -19,7 +19,7 @@ func TestReduceWith(t *testing.T) { {slice: []int{1}, acc: 0, fn: func(a, b int) int { return a + b }, want: 1}, } { t.Run("", func(t *testing.T) { - got := ReduceWith(tt.slice, tt.acc, tt.fn) + got := ReduceFrom(tt.slice, tt.acc, tt.fn) assert.Equal(t, tt.want, got) }) } @@ -43,15 +43,15 @@ func TestReduce(t *testing.T) { func TestSum(t *testing.T) { t.Run("integer", func(t *testing.T) { - assert.Equal(t, Reduce([]int{1, 2}, Sum), 3) - assert.Equal(t, ReduceWith([]int{1, 2}, 22, Sum), 25) + assert.Equal(t, 3, Reduce([]int{1, 2}, Sum)) + assert.Equal(t, 25, ReduceFrom([]int{1, 2}, 22, Sum)) }) t.Run("float", func(t *testing.T) { - assert.Equal(t, Reduce([]float64{1.1, 2.3}, Sum), 3.4) - assert.Equal(t, ReduceWith([]float64{1.1, 2.2}, 22.22, Sum), 25.52) + assert.Equal(t, 3.4, Reduce([]float64{1.1, 2.3}, Sum)) + assert.Equal(t, 25.52, ReduceFrom([]float64{1.1, 2.2}, 22.22, Sum)) }) t.Run("string", func(t *testing.T) { - assert.Equal(t, Reduce([]string{"foo", "bar"}, Sum), "foobar") - assert.Equal(t, ReduceWith([]string{"foo", "bar"}, "buz", Sum), "buzfoobar") + assert.Equal(t, "foobar", Reduce([]string{"foo", "bar"}, Sum)) + assert.Equal(t, "buzfoobar", ReduceFrom([]string{"foo", "bar"}, "buz", Sum)) }) } From c93d47ec84634d2dfeb39ecdb807f9f0e891dfa0 Mon Sep 17 00:00:00 2001 From: Sylvain Prost Date: Mon, 7 Oct 2024 18:03:22 +0200 Subject: [PATCH 3/3] pkg/slices: remove Sum helper --- slices/reduce.go | 10 ---------- slices/reduce_test.go | 15 --------------- 2 files changed, 25 deletions(-) diff --git a/slices/reduce.go b/slices/reduce.go index 8c791bf..1879c1c 100644 --- a/slices/reduce.go +++ b/slices/reduce.go @@ -1,7 +1,5 @@ package slices -import "golang.org/x/exp/constraints" - // ReduceFrom will iterate over each element of slice and call reduceFn for each. // The return value of each reduceFn is passed to the next call of reduceFn as acc, or // in other term at each iteration on the slice. @@ -20,11 +18,3 @@ func Reduce[S ~[]E, E any, A any](slice S, reduceFn func(acc A, elem E) A) A { return ReduceFrom(slice, acc, reduceFn) } - -type sumable interface { - constraints.Integer | constraints.Float | ~string -} - -func Sum[T sumable](i, j T) T { - return i + j -} diff --git a/slices/reduce_test.go b/slices/reduce_test.go index 5ec857a..a57b322 100644 --- a/slices/reduce_test.go +++ b/slices/reduce_test.go @@ -40,18 +40,3 @@ func TestReduce(t *testing.T) { assert.Equal(t, tt.want, got) } } - -func TestSum(t *testing.T) { - t.Run("integer", func(t *testing.T) { - assert.Equal(t, 3, Reduce([]int{1, 2}, Sum)) - assert.Equal(t, 25, ReduceFrom([]int{1, 2}, 22, Sum)) - }) - t.Run("float", func(t *testing.T) { - assert.Equal(t, 3.4, Reduce([]float64{1.1, 2.3}, Sum)) - assert.Equal(t, 25.52, ReduceFrom([]float64{1.1, 2.2}, 22.22, Sum)) - }) - t.Run("string", func(t *testing.T) { - assert.Equal(t, "foobar", Reduce([]string{"foo", "bar"}, Sum)) - assert.Equal(t, "buzfoobar", ReduceFrom([]string{"foo", "bar"}, "buz", Sum)) - }) -}