From e0853ca9709b81defb52fc5afa6934352ebce3d0 Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Tue, 10 May 2022 15:43:34 +0200 Subject: [PATCH] feat: Add FromAnySlice (#133) * feat: adding FromAnySlice helper --- CHANGELOG.md | 7 +++ README.md | 37 ++++++++---- find.go | 6 +- pointers.go | 32 ---------- slice.go | 9 --- slice_test.go | 12 ---- type_manipulation.go | 58 +++++++++++++++++++ pointers_test.go => type_manipulation_test.go | 40 +++++++++++++ 8 files changed, 133 insertions(+), 68 deletions(-) delete mode 100644 pointers.go create mode 100644 type_manipulation.go rename pointers_test.go => type_manipulation_test.go (67%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eaa45c9..8869708b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ @samber: I sometimes forget to update this file. Ping me on [Twitter](https://twitter.com/samuelberthe) or open an issue in case of error. We need to keep a clear changelog for easier lib upgrade. +## 1.21.0 (2022-05-10) + +Adding: + +- lo.ToAnySlice +- lo.FromAnySlice + ## 1.20.0 (2022-05-02) Adding: diff --git a/README.md b/README.md index d9ec42ee..68a1ca65 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,6 @@ Supported helpers for slices: - Reject - Count - CountBy -- ToInterfaceSlice Supported helpers for maps: @@ -144,13 +143,18 @@ Supported search helpers: - Sample - Samples -Other functional programming helpers: +Conditional helpers: - Ternary (1 line if/else statement) - If / ElseIf / Else - Switch / Case / Default + +Type manipulation helpers: + - ToPtr - ToSlicePtr +- ToAnySlice +- FromAnySlice - Empty - Coalesce @@ -636,14 +640,6 @@ slice := lo.ReplaceAll(in, -1, 42) // []int{0, 1, 0, 1, 2, 3, 0} ``` -### ToAnySlice - -Returns a slice with all elements mapped to any type -```go -elements := lo.ToAnySlice[int]([]int{1, 5, 1}) -// []any{1, 5, 1} -``` - ### Keys Creates an array of the map keys. @@ -1336,6 +1332,27 @@ ptr := lo.ToSlicePtr[string]([]string{"hello", "world"}) // []*string{"hello", "world"} ``` +### ToAnySlice + +Returns a slice with all elements mapped to `any` type. + +```go +elements := lo.ToAnySlice[int]([]int{1, 5, 1}) +// []any{1, 5, 1} +``` + +### FromAnySlice + +Returns an `any` slice with all elements mapped to a type. Returns false in case of type conversion failure. + +```go +elements, ok := lo.FromAnySlice[string]([]any{"foobar", 42}) +// []string{}, false + +elements, ok := lo.FromAnySlice[string]([]any{"foobar", "42"}) +// []string{"foobar", "42"}, true +``` + ### Empty Returns an empty value. diff --git a/find.go b/find.go index 8f24e238..bf3b0009 100644 --- a/find.go +++ b/find.go @@ -216,11 +216,7 @@ func Sample[T any](collection []T) T { func Samples[T any](collection []T, count int) []T { size := len(collection) - // put values into a map, for faster deletion - cOpy := make([]T, 0, size) - for _, v := range collection { - cOpy = append(cOpy, v) - } + cOpy := append([]T{}, collection...) results := []T{} diff --git a/pointers.go b/pointers.go deleted file mode 100644 index 623d547d..00000000 --- a/pointers.go +++ /dev/null @@ -1,32 +0,0 @@ -package lo - -// ToPtr returns a pointer copy of value. -func ToPtr[T any](x T) *T { - return &x -} - -// ToSlicePtr returns a slice of pointer copy of value. -func ToSlicePtr[T any](collection []T) []*T { - return Map(collection, func(x T, _ int) *T { - return &x - }) -} - -// Empty returns an empty value. -func Empty[T any]() T { - var t T - return t -} - -// Coalesce returns the first non-empty arguments. Arguments must be comparable. -func Coalesce[T comparable](v ...T) (result T, ok bool) { - for _, e := range v { - if e != result { - result = e - ok = true - return - } - } - - return -} diff --git a/slice.go b/slice.go index 776654be..04d048be 100644 --- a/slice.go +++ b/slice.go @@ -406,12 +406,3 @@ func Replace[T comparable](collection []T, old T, new T, n int) []T { func ReplaceAll[T comparable](collection []T, old T, new T) []T { return Replace[T](collection, old, new, -1) } - -// ToAnySlice returns a slice with all elements mapped to any type -func ToAnySlice[T any](collection []T) []any { - result := make([]any, len(collection)) - for i, item := range collection { - result[i] = item - } - return result -} diff --git a/slice_test.go b/slice_test.go index 345e5f02..cd8066df 100644 --- a/slice_test.go +++ b/slice_test.go @@ -455,15 +455,3 @@ func TestReplaceAll(t *testing.T) { is.Equal([]int{42, 1, 42, 1, 2, 3, 42}, out1) is.Equal([]int{0, 1, 0, 1, 2, 3, 0}, out2) } - -func TestToAnySlice(t *testing.T) { - is := assert.New(t) - - in1 := []int{0, 1, 2, 3} - in2 := []int{} - out1 := ToAnySlice(in1) - out2 := ToAnySlice(in2) - - is.Equal([]any{0, 1, 2, 3}, out1) - is.Equal([]any{}, out2) -} diff --git a/type_manipulation.go b/type_manipulation.go new file mode 100644 index 00000000..3c1ed697 --- /dev/null +++ b/type_manipulation.go @@ -0,0 +1,58 @@ +package lo + +// ToPtr returns a pointer copy of value. +func ToPtr[T any](x T) *T { + return &x +} + +// ToSlicePtr returns a slice of pointer copy of value. +func ToSlicePtr[T any](collection []T) []*T { + return Map(collection, func(x T, _ int) *T { + return &x + }) +} + +// ToAnySlice returns a slice with all elements mapped to `any` type +func ToAnySlice[T any](collection []T) []any { + result := make([]any, len(collection)) + for i, item := range collection { + result[i] = item + } + return result +} + +// FromAnySlice returns an `any` slice with all elements mapped to a type. +// Returns false in case of type conversion failure. +func FromAnySlice[T any](in []any) (out []T, ok bool) { + defer func() { + if r := recover(); r != nil { + out = []T{} + ok = false + } + }() + + result := make([]T, len(in)) + for i, item := range in { + result[i] = item.(T) + } + return result, true +} + +// Empty returns an empty value. +func Empty[T any]() T { + var t T + return t +} + +// Coalesce returns the first non-empty arguments. Arguments must be comparable. +func Coalesce[T comparable](v ...T) (result T, ok bool) { + for _, e := range v { + if e != result { + result = e + ok = true + return + } + } + + return +} diff --git a/pointers_test.go b/type_manipulation_test.go similarity index 67% rename from pointers_test.go rename to type_manipulation_test.go index b6215837..a23baf33 100644 --- a/pointers_test.go +++ b/type_manipulation_test.go @@ -24,6 +24,46 @@ func TestToSlicePtr(t *testing.T) { is.Equal(result1, []*string{&str1, &str2}) } +func TestToAnySlice(t *testing.T) { + is := assert.New(t) + + in1 := []int{0, 1, 2, 3} + in2 := []int{} + out1 := ToAnySlice(in1) + out2 := ToAnySlice(in2) + + is.Equal([]any{0, 1, 2, 3}, out1) + is.Equal([]any{}, out2) +} + +func TestFromAnySlice(t *testing.T) { + is := assert.New(t) + + is.NotPanics(func() { + out1, ok1 := FromAnySlice[string]([]any{"foobar", 42}) + out2, ok2 := FromAnySlice[string]([]any{"foobar", "42"}) + + is.Equal([]string{}, out1) + is.False(ok1) + is.Equal([]string{"foobar", "42"}, out2) + is.True(ok2) + }) +} + +func TestEmpty(t *testing.T) { + is := assert.New(t) + + //nolint:unused + type test struct { + foobar string + } + + is.Empty(Empty[string]()) + is.Empty(Empty[int64]()) + is.Empty(Empty[test]()) + is.Empty(Empty[chan string]()) +} + func TestCoalesce(t *testing.T) { is := assert.New(t)