From f68aaf7578ac408d7840ce21d6ccac0d3b4be322 Mon Sep 17 00:00:00 2001 From: KylinDC Date: Sat, 30 Dec 2023 11:28:10 +0800 Subject: [PATCH] feat: add ToSorted function to return a sorted copy of a slice --- README.md | 12 ++++++++++++ slice.go | 14 ++++++++++++++ slice_benchmark_test.go | 19 +++++++++++++++++++ slice_example_test.go | 10 ++++++++++ slice_test.go | 23 +++++++++++++++++++++-- 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dd848c39..1cbf2c09 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ Supported helpers for slices: - [Compact](#compact) - [IsSorted](#issorted) - [IsSortedByKey](#issortedbykey) +- [ToSorted](#tosorted) Supported helpers for maps: @@ -924,6 +925,17 @@ slice := lo.IsSortedByKey([]string{"a", "bb", "ccc"}, func(s string) int { [[play](https://go.dev/play/p/wiG6XyBBu49)] +### ToSorted + +Return a sorted copy of a slice. + +```go +sorted := lo.ToSorted([]int{0, 1, 4, 3, 2}) +// [0 1 2 3 4] +``` + +[[play](https://go.dev/play/p/G4-aR2Yxh3M)] + ### Keys Creates an array of the map keys. diff --git a/slice.go b/slice.go index 4bfed2de..24a97ad7 100644 --- a/slice.go +++ b/slice.go @@ -4,6 +4,7 @@ import ( "math/rand" "golang.org/x/exp/constraints" + "golang.org/x/exp/slices" ) // Filter iterates over elements of collection, returning an array of all elements predicate returns truthy for. @@ -592,3 +593,16 @@ func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(i return true } + +// ToSorted return a sorted copy of a slice +// Play: https://go.dev/play/p/G4-aR2Yxh3M +func ToSorted[T constraints.Ordered](collection []T) []T { + if collection == nil { + return nil + } + + copied := make([]T, len(collection)) + copy(copied, collection) + slices.Sort(copied) + return copied +} diff --git a/slice_benchmark_test.go b/slice_benchmark_test.go index 38911c08..ee9c3805 100644 --- a/slice_benchmark_test.go +++ b/slice_benchmark_test.go @@ -171,3 +171,22 @@ func BenchmarkReplace(b *testing.B) { }) } } + +func BenchmarkToSorted(b *testing.B) { + lengths := []int{1_000, 10_000, 100_000} + for _, n := range lengths { + strs := genSliceString(n) + b.Run(fmt.Sprintf("strings_%d", n), func(b *testing.B) { + _ = ToSorted(strs) + }) + } + + for _, n := range lengths { + ints := genSliceInt(n) + b.Run(fmt.Sprintf("ints%d", n), func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = ToSorted(ints) + } + }) + } +} diff --git a/slice_example_test.go b/slice_example_test.go index b5b49d2b..270caa6a 100644 --- a/slice_example_test.go +++ b/slice_example_test.go @@ -456,3 +456,13 @@ func ExampleIsSortedByKey() { // Output: true } + +func ExampleToSorted() { + list := []int{0, 1, 4, 3, 2} + + result := ToSorted(list) + + fmt.Printf("%v", result) + + // Output: [0 1 2 3 4] +} diff --git a/slice_test.go b/slice_test.go index 03326a01..25d822be 100644 --- a/slice_test.go +++ b/slice_test.go @@ -388,7 +388,7 @@ func TestAssociate(t *testing.T) { func TestSliceToMap(t *testing.T) { t.Parallel() - + type foo struct { baz string bar int @@ -626,7 +626,7 @@ func TestSlice(t *testing.T) { out16 := Slice(in, -10, 1) out17 := Slice(in, -1, 3) out18 := Slice(in, -10, 7) - + is.Equal([]int{}, out1) is.Equal([]int{0}, out2) is.Equal([]int{0, 1, 2, 3, 4}, out3) @@ -759,3 +759,22 @@ func TestIsSortedByKey(t *testing.T) { return ret })) } + +func TestToSorted(t *testing.T) { + t.Parallel() + is := assert.New(t) + + s1 := []int{1, 2, 5, 3, 2} + s2 := []int{1, 2, 2, 3, 5} + + r1 := ToSorted(s1) + is.Equal(s2, r1) + is.NotEqual(&s1, &r1) + + s3 := []string{"a", "c", "b", "e", "d"} + s4 := []string{"a", "b", "c", "d", "e"} + + r2 := ToSorted(s3) + is.Equal(s4, r2) + is.NotEqual(&s3, &r2) +}