From 746e473e98ee9d9eb62eecdecb91e570c570583b Mon Sep 17 00:00:00 2001 From: Momchil Atanasov Date: Sun, 3 Mar 2024 18:42:47 +0200 Subject: [PATCH] Code, API and Docu improvements (#29) --- README.md | 10 ++++++---- slice.go | 23 +++++++++++++++++++++++ slice_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ value.go | 7 +++++++ value_test.go | 22 ++++++++++++++++++++++ 5 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 value.go create mode 100644 value_test.go diff --git a/README.md b/README.md index 7d01048..2b0ac50 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ # GoG (Go Generics) +[![Go Reference](https://pkg.go.dev/badge/github.com/mokiat/gog.svg)](https://pkg.go.dev/github.com/mokiat/gog) +[![Go Report Card](https://goreportcard.com/badge/github.com/mokiat/gog)](https://goreportcard.com/report/github.com/mokiat/gog) + GoG is a Go library with useful generic functions and types. Since the introduction of generics in Go 1.18, a number of useful and reusable data structures, algorithms and utility functions are now possible. This library attempts to cover some of the most common use cases. -It does not include functions that are already provided by -[x/exp/slices](https://pkg.go.dev/golang.org/x/exp/slices) and -[x/exp/maps](https://pkg.go.dev/golang.org/x/exp/maps), both of which might -soon be part of the built-in packages. +It avoids duplicating functions that are already provided by +[slices](https://pkg.go.dev/slices) and +[maps](https://pkg.go.dev/maps). For a complete list on available functions and types, check the godoc documentation for this project: diff --git a/slice.go b/slice.go index 7e2f56b..deb83f1 100644 --- a/slice.go +++ b/slice.go @@ -15,6 +15,19 @@ func Map[S, T any](slice []S, fn func(S) T) []T { return result } +// MapIndex is similar to Map, except that it passes the element index +// to the closure function as well. +func MapIndex[S, T any](slice []S, fn func(int, S) T) []T { + if slice == nil { + return nil + } + result := make([]T, len(slice)) + for i, v := range slice { + result[i] = fn(i, v) + } + return result +} + // Reduce compacts a slice into a single value. The provided function is used // to perform the reduction starting with the initialValue. func Reduce[S, T any](slice []S, initialValue T, fn func(accum T, value S) T) T { @@ -98,6 +111,14 @@ func Mutate[T any](slice []T, fn func(e *T)) { } } +// MutateIndex is similar to Mutate, except that it passes the element index +// to the closure function as well. +func MutateIndex[T any](slice []T, fn func(index int, e *T)) { + for i := range slice { + fn(i, &slice[i]) + } +} + // FindFunc iterates over the slice and uses the provided closure function // to check whether the elements match a user-provided condition. The first // value that matches is returned as well as a true flag. Otherwise a @@ -150,6 +171,8 @@ func DerefElements[T any](slice []*T) []T { // // This function always allocates a brand new slice with appropriate // capacity and never mutates any of the passed slices. +// +// Deprecated: Use built-in slices.Concat instead. func Concat[T any](slices ...[]T) []T { capacity := 0 for _, slice := range slices { diff --git a/slice_test.go b/slice_test.go index 626f8ec..32a761c 100644 --- a/slice_test.go +++ b/slice_test.go @@ -29,6 +29,24 @@ var _ = Describe("Slice", func() { }) }) + Describe("MapIndex", func() { + mapFunc := func(index, v int) string { + return strconv.Itoa(v * index) + } + + It("converts from one slice type to another", func() { + source := []int{1, 2, 3} + target := gog.MapIndex(source, mapFunc) + Expect(target).To(Equal([]string{ + "0", "2", "6", + })) + }) + + It("preserves the nil slice", func() { + Expect(gog.MapIndex(nil, mapFunc)).To(Equal([]string(nil))) + }) + }) + Describe("Reduce", func() { It("reduces a slice to a single value", func() { source := []int{1, 2, 3} @@ -144,6 +162,30 @@ var _ = Describe("Slice", func() { }) }) + Describe("MutateIndex", func() { + timesIndex := func(index int, v *int) { + *v *= index + } + + It("mutates the items of a slice", func() { + slice := []int{1, 2, 3, 4} + gog.MutateIndex(slice, timesIndex) + Expect(slice).To(Equal([]int{0, 2, 6, 12})) + }) + + It("ignores empty slices", func() { + slice := []int{} + gog.MutateIndex(slice, timesIndex) + Expect(slice).To(Equal([]int{})) + }) + + It("ignores nil slices", func() { + var slice []int + gog.MutateIndex(slice, timesIndex) + Expect(slice).To(Equal([]int(nil))) + }) + }) + Describe("FindFunc", func() { divisibleByFive := func(v int) bool { return v%5 == 0 diff --git a/value.go b/value.go new file mode 100644 index 0000000..d97e121 --- /dev/null +++ b/value.go @@ -0,0 +1,7 @@ +package gog + +// Zero returns the zero value of the generic type T. +func Zero[T any]() T { + var zero T + return zero +} diff --git a/value_test.go b/value_test.go new file mode 100644 index 0000000..f0dbd13 --- /dev/null +++ b/value_test.go @@ -0,0 +1,22 @@ +package gog_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/mokiat/gog" +) + +var _ = Describe("Value", func() { + + Describe("Zero", func() { + + It("returns the zero value", func() { + Expect(gog.Zero[int]()).To(Equal(0)) + Expect(gog.Zero[string]()).To(Equal("")) + Expect(gog.Zero[bool]()).To(Equal(false)) + }) + + }) + +})