diff --git a/lancetconstraints/constraints.go b/lancetconstraints/constraints.go index 87e50be..a2ba82e 100644 --- a/lancetconstraints/constraints.go +++ b/lancetconstraints/constraints.go @@ -16,3 +16,8 @@ type Comparator interface { type Number interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 } + +// Ordered is a constraint that permits any ordered type: any type that supports the operators < <= >= > +type Ordered interface { + Number | ~string +} diff --git a/slice/slice.go b/slice/slice.go index 8cd9b47..db50fc2 100644 --- a/slice/slice.go +++ b/slice/slice.go @@ -10,6 +10,8 @@ import ( "math/rand" "reflect" "sort" + + "github.com/duke-git/lancet/v2/lancetconstraints" ) // Contain check if the target value is in the slice or not @@ -713,8 +715,18 @@ func Shuffle[T any](slice []T) []T { return result } +// Sort sorts a slice of any ordered type(number or string), use quick sort algrithm. +// default sort order is ascending (asc), if want descending order, set param `sortOrder` to `desc` +func Sort[T lancetconstraints.Ordered](slice []T, sortOrder ...string) { + if len(sortOrder) > 0 && sortOrder[0] == "desc" { + quickSort(slice, 0, len(slice)-1, "desc") + } else { + quickSort(slice, 0, len(slice)-1, "asc") + } +} + // SortByField return sorted slice by field -// Slice element should be struct, field type should be int, uint, string, or bool +// slice element should be struct, field type should be int, uint, string, or bool // default sortType is ascending (asc), if descending order, set sortType to desc func SortByField(slice any, field string, sortType ...string) error { sv := sliceValue(slice) diff --git a/slice/slice_internal.go b/slice/slice_internal.go index 30f43db..9ff8996 100644 --- a/slice/slice_internal.go +++ b/slice/slice_internal.go @@ -3,6 +3,8 @@ package slice import ( "fmt" "reflect" + + "github.com/duke-git/lancet/v2/lancetconstraints" ) // sliceValue return the reflect value of a slice @@ -24,3 +26,40 @@ func sliceElemType(reflectType reflect.Type) reflect.Type { reflectType = reflectType.Elem() } } + +func quickSort[T lancetconstraints.Ordered](slice []T, lowIndex, highIndex int, order string) { + if lowIndex < highIndex { + p := partition(slice, lowIndex, highIndex, order) + quickSort(slice, lowIndex, p-1, order) + quickSort(slice, p+1, highIndex, order) + } +} + +// partition split slice into two parts for quick sort +func partition[T lancetconstraints.Ordered](slice []T, lowIndex, highIndex int, order string) int { + p := slice[highIndex] + i := lowIndex + + for j := lowIndex; j < highIndex; j++ { + if order == "desc" { + if slice[j] > p { + swap(slice, i, j) + i++ + } + } else { + if slice[j] < p { + swap(slice, i, j) + i++ + } + } + } + + swap(slice, i, highIndex) + + return i +} + +// swap two slice value at index i and j +func swap[T any](slice []T, i, j int) { + slice[i], slice[j] = slice[j], slice[i] +} diff --git a/slice/slice_test.go b/slice/slice_test.go index 65b6359..e0fcb03 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -538,6 +538,26 @@ func TestDifferenceBy(t *testing.T) { assert.Equal([]int{1, 2}, DifferenceBy(s1, s2, addOne)) } +func TestSort(t *testing.T) { + assert := internal.NewAssert(t, "TestSort") + + numbers := []int{1, 4, 3, 2, 5} + + Sort(numbers) + assert.Equal([]int{1, 2, 3, 4, 5}, numbers) + + Sort(numbers, "desc") + assert.Equal([]int{5, 4, 3, 2, 1}, numbers) + + strings := []string{"a", "d", "c", "b", "e"} + + Sort(strings) + assert.Equal([]string{"a", "b", "c", "d", "e"}, strings) + + Sort(strings, "desc") + assert.Equal([]string{"e", "d", "c", "b", "a"}, strings) +} + func TestSortByFielDesc(t *testing.T) { assert := internal.NewAssert(t, "TestSortByFielDesc")