From e245fb76de36463b715e1ac2bb8193670bb26f0d Mon Sep 17 00:00:00 2001 From: Cory Snider Date: Wed, 24 Jan 2024 17:56:29 -0500 Subject: [PATCH] internal/sliceutil: add utilities to map values Functional programming for the win! Add a utility function to map the values of a slice, along with a curried variant, to tide us over until equivalent functionality gets added to the standard library (https://go.dev/issue/61898) Signed-off-by: Cory Snider --- internal/sliceutil/sliceutil.go | 18 ++++++++++ internal/sliceutil/sliceutil_test.go | 49 ++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 internal/sliceutil/sliceutil_test.go diff --git a/internal/sliceutil/sliceutil.go b/internal/sliceutil/sliceutil.go index 66d90f2dc9..6e3967c927 100644 --- a/internal/sliceutil/sliceutil.go +++ b/internal/sliceutil/sliceutil.go @@ -11,3 +11,21 @@ func Dedup[T comparable](slice []T) []T { } return out } + +func Map[S ~[]In, In, Out any](s S, fn func(In) Out) []Out { + res := make([]Out, len(s)) + for i, v := range s { + res[i] = fn(v) + } + return res +} + +func Mapper[In, Out any](fn func(In) Out) func([]In) []Out { + return func(s []In) []Out { + res := make([]Out, len(s)) + for i, v := range s { + res[i] = fn(v) + } + return res + } +} diff --git a/internal/sliceutil/sliceutil_test.go b/internal/sliceutil/sliceutil_test.go new file mode 100644 index 0000000000..ab14194798 --- /dev/null +++ b/internal/sliceutil/sliceutil_test.go @@ -0,0 +1,49 @@ +package sliceutil_test + +import ( + "net/netip" + "strconv" + "testing" + + "github.com/docker/docker/internal/sliceutil" +) + +func TestMap(t *testing.T) { + s := []int{1, 2, 3} + m := sliceutil.Map(s, func(i int) int { return i * 2 }) + if len(m) != len(s) { + t.Fatalf("expected len %d, got %d", len(s), len(m)) + } + for i, v := range m { + if expected := s[i] * 2; v != expected { + t.Fatalf("expected %d, got %d", expected, v) + } + } +} + +func TestMap_TypeConvert(t *testing.T) { + s := []int{1, 2, 3} + m := sliceutil.Map(s, func(i int) string { return strconv.Itoa(i) }) + if len(m) != len(s) { + t.Fatalf("expected len %d, got %d", len(s), len(m)) + } + for i, v := range m { + if expected := strconv.Itoa(s[i]); v != expected { + t.Fatalf("expected %s, got %s", expected, v) + } + } +} + +func TestMapper(t *testing.T) { + s := []string{"1.2.3.4", "fe80::1"} + mapper := sliceutil.Mapper(netip.MustParseAddr) + m := mapper(s) + if len(m) != len(s) { + t.Fatalf("expected len %d, got %d", len(s), len(m)) + } + for i, v := range m { + if expected := netip.MustParseAddr(s[i]); v != expected { + t.Fatalf("expected %s, got %s", expected, v) + } + } +}