Skip to content

Commit 54f88d2

Browse files
author
hay.123
committed
add partial sort for gslice
1 parent 7c2e868 commit 54f88d2

4 files changed

Lines changed: 88 additions & 0 deletions

File tree

gslice/gslice.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,11 @@ func StableSortBy[T any](s []T, less func(T, T) bool) {
10111011
_ = iter.ToSlice(iter.StableSortBy(less, iter.StealSlice(s)))
10121012
}
10131013

1014+
// PartialSort
1015+
func PartialSort[T constraints.Ordered](s []T, k int) {
1016+
_ = iter.ToSlice(iter.PartialSort(k, iter.StealSlice(s)))
1017+
}
1018+
10141019
// TypeAssert converts a slice from type From to type To by type assertion.
10151020
//
10161021
// 🚀 EXAMPLE:

gslice/gslice_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,50 @@ func TestStableSortBy(t *testing.T) {
623623
}
624624
}
625625

626+
func TestPartialSort(t *testing.T) {
627+
// Basic case - first k elements sorted and smallest
628+
{
629+
s := []int{5, 2, 9, 1, 5, 6}
630+
PartialSort(s, 3)
631+
assert.Equal(t, []int{1, 2, 5, 9, 5, 6}, s) // First 3 are smallest + sorted
632+
}
633+
634+
// k == length (full sort)
635+
{
636+
s := []int{3, 1, 4}
637+
PartialSort(s, 3)
638+
assert.Equal(t, []int{1, 3, 4}, s) // Fully sorted
639+
}
640+
641+
// k > length (behaves like full sort)
642+
{
643+
s := []int{7, 3}
644+
PartialSort(s, 5)
645+
assert.Equal(t, []int{3, 7}, s) // Treats as full sort
646+
}
647+
648+
// Empty slice
649+
{
650+
s := []int{}
651+
PartialSort(s, 2)
652+
assert.Equal(t, []int{}, s) // No panic
653+
}
654+
655+
// k == 0 (no-op)
656+
{
657+
s := []int{5, 2, 8}
658+
PartialSort(s, 0)
659+
assert.Equal(t, []int{5, 2, 8}, s) // Unmodified
660+
}
661+
662+
// Already sorted
663+
{
664+
s := []int{1, 2, 3, 4, 5}
665+
PartialSort(s, 3)
666+
assert.Equal(t, []int{1, 2, 3, 4, 5}, s) // Unchanged
667+
}
668+
}
669+
626670
func TestTypeAssert(t *testing.T) {
627671
assert.Equal(t, []int{1, 2, 3, 4}, TypeAssert[int, any]([]any{1, 2, 3, 4}))
628672
assert.Equal(t, []any{1, 2, 3, 4}, TypeAssert[any, int]([]int{1, 2, 3, 4}))

internal/heapsort/sort.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,32 @@ func Sort[T constraints.Ordered](v []T) {
4848
siftDown(v[:i], 0)
4949
}
5050
}
51+
52+
func PartialSort[T constraints.Ordered](v []T, k int) {
53+
n := len(v)
54+
55+
if k <= 0 {
56+
return
57+
}
58+
59+
if k >= n {
60+
Sort(v)
61+
return
62+
}
63+
64+
// Build a max-heap from the first k elements
65+
for j := (k - 1) / 2; j >= 0; j-- {
66+
siftDown(v[:k], j)
67+
}
68+
69+
// Iterate through the rest of the slice
70+
for j := k; j < n; j++ {
71+
if v[j] < v[0] {
72+
v[0], v[j] = v[j], v[0]
73+
siftDown(v[:k], 0)
74+
}
75+
}
76+
77+
// Sort the heap to get the final k smallest elements in order
78+
Sort(v[:k])
79+
}

internal/iter/operations.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,16 @@ func Sort[T constraints.Ordered](i Iter[T]) Iter[T] {
923923
return StealSlice(s)
924924
}
925925

926+
// PartialSort sorts the first k smallest elements in ascending order, with the remaining elements left unordered,
927+
// and returns a new iterator.
928+
// - If k <= 0, returns the entire slice unmodified.
929+
// - If k >= len(slice), performs a full sort.
930+
func PartialSort[T constraints.Ordered](k int, iter Iter[T]) Iter[T] {
931+
s := ToSlice(iter)
932+
heapsort.PartialSort(s, k)
933+
return StealSlice(s)
934+
}
935+
926936
// Contains returns whether the element occur in iterator.
927937
func Contains[T comparable](v T, i Iter[T]) bool {
928938
for _, vv := range i.Next(ALL) {

0 commit comments

Comments
 (0)