Skip to content

Commit

Permalink
added: SortStableUsing and SortUsing (#103)
Browse files Browse the repository at this point in the history
SortStableUsing works similar to sort.SliceStable. However, unlike sort.SliceStable the slice returned will be reallocated as to not modify the input slice.

SortUsing works similar to sort.Slice. However, unlike sort.Slice the slice returned will be reallocated as to not modify the input slice.

Fixes #98
  • Loading branch information
Deleplace authored and elliotchance committed May 7, 2019
1 parent 55d5be7 commit aa5d6ff
Show file tree
Hide file tree
Showing 14 changed files with 416 additions and 5 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ This will only generate `myInts.Average`, `myInts.Sum` and `myStrings.Filter`.
| `Reverse` |||| | n | Reverse elements. |
| `Send` |||| | n | Send all element to channel. |
| `Sort` ||| | | n⋅log(n) | Return a new sorted slice. |
| `SortUsing` || || | n⋅log(n) | Return a new sorted slice, using custom comparator. |
| `SortStableUsing` || || | n⋅log(n) | Return a new sorted slice, using custom comparator, keeping the original order of equal elements. |
| `Sum` | || | | n | Sum (total) of all elements. |
| `Shuffle` |||| | n | Returns a new shuffled slice. |
| `Top` |||| | n | Gets several elements from top(head of slice).|
Expand Down
2 changes: 2 additions & 0 deletions functions/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ var Functions = []struct {
{"Reverse", "reverse.go", ForAll},
{"Send", "send.go", ForAll},
{"Sort", "sort.go", ForNumbersAndStrings},
{"SortUsing", "sort_using.go", ForStrings | ForStructs},
{"SortStableUsing", "sort_stable_using.go", ForStrings | ForStructs},
{"Sum", "sum.go", ForNumbers},
{"Shuffle", "shuffle.go", ForAll},
{"Top", "top.go", ForAll},
Expand Down
2 changes: 1 addition & 1 deletion functions/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func (ss SliceType) Sort() SliceType {
return ss
}

sorted := make([]ElementType, len(ss))
sorted := make(SliceType, len(ss))
copy(sorted, ss)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i] < sorted[j]
Expand Down
23 changes: 23 additions & 0 deletions functions/sort_stable_using.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package functions

import (
"sort"
)

// SortStableUsing works similar to sort.SliceStable. However, unlike sort.SliceStable the
// slice returned will be reallocated as to not modify the input slice.
func (ss SliceType) SortStableUsing(less func(a, b ElementType) bool) SliceType {
// Avoid the allocation. If there is one element or less it is already
// sorted.
if len(ss) < 2 {
return ss
}

sorted := make(SliceType, len(ss))
copy(sorted, ss)
sort.SliceStable(sorted, func(i, j int) bool {
return less(sorted[i], sorted[j])
})

return sorted
}
23 changes: 23 additions & 0 deletions functions/sort_using.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package functions

import (
"sort"
)

// SortUsing works similar to sort.Slice. However, unlike sort.Slice the
// slice returned will be reallocated as to not modify the input slice.
func (ss SliceType) SortUsing(less func(a, b ElementType) bool) SliceType {
// Avoid the allocation. If there is one element or less it is already
// sorted.
if len(ss) < 2 {
return ss
}

sorted := make(SliceType, len(ss))
copy(sorted, ss)
sort.Slice(sorted, func(i, j int) bool {
return less(sorted[i], sorted[j])
})

return sorted
}
37 changes: 37 additions & 0 deletions pie/carpointers_pie.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"github.com/elliotchance/pie/pie/util"
"math/rand"
"sort"
)

// All will return true if all callbacks return true. It follows the same logic
Expand Down Expand Up @@ -259,6 +260,42 @@ func (ss carPointers) Send(ctx context.Context, ch chan<- *car) carPointers {
return ss
}

// SortUsing works similar to sort.Slice. However, unlike sort.Slice the
// slice returned will be reallocated as to not modify the input slice.
func (ss carPointers) SortUsing(less func(a, b *car) bool) carPointers {
// Avoid the allocation. If there is one element or less it is already
// sorted.
if len(ss) < 2 {
return ss
}

sorted := make(carPointers, len(ss))
copy(sorted, ss)
sort.Slice(sorted, func(i, j int) bool {
return less(sorted[i], sorted[j])
})

return sorted
}

// SortStableUsing works similar to sort.SliceStable. However, unlike sort.SliceStable the
// slice returned will be reallocated as to not modify the input slice.
func (ss carPointers) SortStableUsing(less func(a, b *car) bool) carPointers {
// Avoid the allocation. If there is one element or less it is already
// sorted.
if len(ss) < 2 {
return ss
}

sorted := make(carPointers, len(ss))
copy(sorted, ss)
sort.SliceStable(sorted, func(i, j int) bool {
return less(sorted[i], sorted[j])
})

return sorted
}

// Shuffle returns shuffled slice by your rand.Source
func (ss carPointers) Shuffle(source rand.Source) carPointers {
n := len(ss)
Expand Down
72 changes: 72 additions & 0 deletions pie/carpointers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,78 @@ var stringsToStringsTests = []struct {
},
}

var carPointersSortCustomTests = []struct {
ss carPointers
sortedStableByName carPointers
sortedStableByColor carPointers
}{
{
nil,
nil,
nil,
},
{
carPointers{},
carPointers{},
carPointers{},
},
{
carPointers{&car{"foo", "red"}},
carPointers{&car{"foo", "red"}},
carPointers{&car{"foo", "red"}},
},
{
carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}, &car{"foo", "red"}},
carPointers{&car{"Baz", "black"}, &car{"bar", "yellow"}, &car{"foo", "red"}},
carPointers{&car{"Baz", "black"}, &car{"foo", "red"}, &car{"bar", "yellow"}},
},
{
carPointers{&car{"bar", "yellow"}, &car{"Baz", "black"}, &car{"qux", "cyan"}, &car{"foo", "red"}},
carPointers{&car{"Baz", "black"}, &car{"bar", "yellow"}, &car{"foo", "red"}, &car{"qux", "cyan"}},
carPointers{&car{"Baz", "black"}, &car{"qux", "cyan"}, &car{"foo", "red"}, &car{"bar", "yellow"}},
},
{
carPointers{&car{"aaa", "yellow"}, &car{"aaa", "black"}, &car{"bbb", "yellow"}, &car{"bbb", "black"}},
carPointers{&car{"aaa", "yellow"}, &car{"aaa", "black"}, &car{"bbb", "yellow"}, &car{"bbb", "black"}},
carPointers{&car{"aaa", "black"}, &car{"bbb", "black"}, &car{"aaa", "yellow"}, &car{"bbb", "yellow"}},
},
}

func carPointerNameLess(a, b *car) bool {
return a.Name < b.Name
}

func carPointerColorLess(a, b *car) bool {
return a.Color < b.Color
}

func TestCarPointers_SortUsing(t *testing.T) {
isSortedUsing := func(ss carPointers, less func(a, b *car) bool) bool {
for i := 1; i < len(ss); i++ {
if less(ss[i], ss[i-1]) {
return false
}
}
return true
}

for _, test := range carPointersSortCustomTests {
t.Run("", func(t *testing.T) {
defer assertImmutableCarPointers(t, &test.ss)()

sortedByName := test.ss.SortUsing(carPointerNameLess)
assert.True(t, isSortedUsing(sortedByName, carPointerNameLess))
sortedStableByName := test.ss.SortStableUsing(carPointerNameLess)
assert.Equal(t, test.sortedStableByName, sortedStableByName)

sortedByColor := test.ss.SortUsing(carPointerColorLess)
assert.True(t, isSortedUsing(sortedByColor, carPointerColorLess))
sortedStableByColor := test.ss.SortStableUsing(carPointerColorLess)
assert.Equal(t, test.sortedStableByColor, sortedStableByColor)
})
}
}

func TestCarPointers_ToStrings(t *testing.T) {
for _, test := range stringsToStringsTests {
t.Run("", func(t *testing.T) {
Expand Down
37 changes: 37 additions & 0 deletions pie/cars_pie.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"github.com/elliotchance/pie/pie/util"
"math/rand"
"sort"
)

// All will return true if all callbacks return true. It follows the same logic
Expand Down Expand Up @@ -259,6 +260,42 @@ func (ss cars) Send(ctx context.Context, ch chan<- car) cars {
return ss
}

// SortUsing works similar to sort.Slice. However, unlike sort.Slice the
// slice returned will be reallocated as to not modify the input slice.
func (ss cars) SortUsing(less func(a, b car) bool) cars {
// Avoid the allocation. If there is one element or less it is already
// sorted.
if len(ss) < 2 {
return ss
}

sorted := make(cars, len(ss))
copy(sorted, ss)
sort.Slice(sorted, func(i, j int) bool {
return less(sorted[i], sorted[j])
})

return sorted
}

// SortStableUsing works similar to sort.SliceStable. However, unlike sort.SliceStable the
// slice returned will be reallocated as to not modify the input slice.
func (ss cars) SortStableUsing(less func(a, b car) bool) cars {
// Avoid the allocation. If there is one element or less it is already
// sorted.
if len(ss) < 2 {
return ss
}

sorted := make(cars, len(ss))
copy(sorted, ss)
sort.SliceStable(sorted, func(i, j int) bool {
return less(sorted[i], sorted[j])
})

return sorted
}

// Shuffle returns shuffled slice by your rand.Source
func (ss cars) Shuffle(source rand.Source) cars {
n := len(ss)
Expand Down
72 changes: 72 additions & 0 deletions pie/cars_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,78 @@ func TestCars_Reverse(t *testing.T) {
}
}

var carsSortCustomTests = []struct {
ss cars
sortedStableByName cars
sortedStableByColor cars
}{
{
nil,
nil,
nil,
},
{
cars{},
cars{},
cars{},
},
{
cars{car{"foo", "red"}},
cars{car{"foo", "red"}},
cars{car{"foo", "red"}},
},
{
cars{car{"bar", "yellow"}, car{"Baz", "black"}, car{"foo", "red"}},
cars{car{"Baz", "black"}, car{"bar", "yellow"}, car{"foo", "red"}},
cars{car{"Baz", "black"}, car{"foo", "red"}, car{"bar", "yellow"}},
},
{
cars{car{"bar", "yellow"}, car{"Baz", "black"}, car{"qux", "cyan"}, car{"foo", "red"}},
cars{car{"Baz", "black"}, car{"bar", "yellow"}, car{"foo", "red"}, car{"qux", "cyan"}},
cars{car{"Baz", "black"}, car{"qux", "cyan"}, car{"foo", "red"}, car{"bar", "yellow"}},
},
{
cars{car{"aaa", "yellow"}, car{"aaa", "black"}, car{"bbb", "yellow"}, car{"bbb", "black"}},
cars{car{"aaa", "yellow"}, car{"aaa", "black"}, car{"bbb", "yellow"}, car{"bbb", "black"}},
cars{car{"aaa", "black"}, car{"bbb", "black"}, car{"aaa", "yellow"}, car{"bbb", "yellow"}},
},
}

func carNameLess(a, b car) bool {
return a.Name < b.Name
}

func carColorLess(a, b car) bool {
return a.Color < b.Color
}

func TestCars_SortUsing(t *testing.T) {
isSortedUsing := func(ss cars, less func(a, b car) bool) bool {
for i := 1; i < len(ss); i++ {
if less(ss[i], ss[i-1]) {
return false
}
}
return true
}

for _, test := range carsSortCustomTests {
t.Run("", func(t *testing.T) {
defer assertImmutableCars(t, &test.ss)()

sortedByName := test.ss.SortUsing(carNameLess)
assert.True(t, isSortedUsing(sortedByName, carNameLess))
sortedStableByName := test.ss.SortStableUsing(carNameLess)
assert.Equal(t, test.sortedStableByName, sortedStableByName)

sortedByColor := test.ss.SortUsing(carColorLess)
assert.True(t, isSortedUsing(sortedByColor, carColorLess))
sortedStableByColor := test.ss.SortStableUsing(carColorLess)
assert.Equal(t, test.sortedStableByColor, sortedStableByColor)
})
}
}

var carsToStringsTests = []struct {
ss cars
transform func(car) string
Expand Down
2 changes: 1 addition & 1 deletion pie/float64s_pie.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ func (ss Float64s) Sort() Float64s {
return ss
}

sorted := make([]float64, len(ss))
sorted := make(Float64s, len(ss))
copy(sorted, ss)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i] < sorted[j]
Expand Down
2 changes: 1 addition & 1 deletion pie/ints_pie.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ func (ss Ints) Sort() Ints {
return ss
}

sorted := make([]int, len(ss))
sorted := make(Ints, len(ss))
copy(sorted, ss)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i] < sorted[j]
Expand Down
Loading

0 comments on commit aa5d6ff

Please sign in to comment.