Generic Map Functions in Go.
Javascript arrays have foreEach() method (which I honestly miss in Go).
It's an iterative method, that calls a provided callback function once for each element in an array in
ascending-index order.
The Map Functions pattern in Go pretty closely resembles this Javascript method, - I suspect it
is called this way because the callback gets mapped to each element.
With introduction of generics,
implementation of this pattern has become as easy as it's never been.
File go-sketches.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package go_sketches // implement 'foreach' function from JS // using generics type Array[T any] []T // ForEach resembles Javascript arrays' "forEach" method // it's also a good exercise in pointers func (a Array[T]) ForEach(f func(item *T)) { for i, _ := range a { // process everything in place, so the space complexity is O(n), // not O(2n) ;-) // remember: in the loop any collection item is passed by value! // this is why we're getting each element by its index, and then // using pointer to it f(&(a[i])) } } |
[T any] in the type definition is a type constraint, and our
newly defined type Array can contain elements of any
type (still, all the elements have to be of the same type).
Sometimes this is mistakenly confused with interface{} type, which
is incorrect. Generic type constraints have nothing to do with empty interface, and it's the
compiler's job to infer the concrete type of a generic element.
Similar syntax is used in our ForEach method definition,
where *T indicates that
the method expects a pointer to the data of any type as its argument.
And now let's make sure that everything works as expected. For unit testing I'm using great
Testify package.
File
sketches_test.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package go_sketches import ( "testing" "github.com/stretchr/testify/assert" ) // nice exercise in pointers as well func SquareInt(i *int) { *i = (*i) * (*i) } func TestArray_ForEach(t *testing.T) { a := Array[int]{1, 2, 3, 4, 5} a.ForEach(SquareInt) exp := []int{1, 4, 9, 16, 25} assert.EqualValues(t, exp, a) } |
Run the test - it worked; I will leave to the reader to implement callback functions (along with SquareInt) with another types of arguments and supplement our test with such functions.