customize sorting in go [duplicate] - sorting

This question already has answers here:
Go composite literal for type of primitive value
(1 answer)
Go: cast slice of primitive to a slice of its alias [duplicate]
(1 answer)
Closed 9 months ago.
type ByHeight []golfTree
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByHeight) Len() int { return len(a) }
func (s ByHeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ByHeight) Less(i, j int) bool { return s[i].height < s[j].height }
func test() {
p := make([]Person, 10, 10)
sort.Sort(ByAge(p))
f := make([]golfTree, 10, 10)
***sort.Sort(ByHeight{f})***
}
Here is the code that sort for two slice, but the ByHeight sort always raise an error. It says that
Cannot use 'f' (type []golfTree) as the type golfTree
But for person and tree, I nearly do the same thing. What's the error ?

Related

How to sort slice according to the numeric value and if numeric value equals then sort by alphabetical order

I have slice as below
{string, int }
[{zaa 1} {aab 1} {xac 1}]
in this case int side equal so no I need to sort using alphabetical order
if my slice like bellow
[{zaa 1} {aab 4} {xac 2}]
I need to sort using numeric value, how can I do this?
Right now I'm using sort given by golang
type ByStringValue []string
type ByNumericValue []WeightBaseResourceInfo
func (a ByStringValue) Len() int { return len(a) }
func (a ByStringValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByStringValue) Less(i, j int) bool { return a[i] < a[j] }
func (a ByNumericValue) Len() int { return len(a) }
func (a ByNumericValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByNumericValue) Less(i, j int) bool {
w1 := a[i].Weight
w2 := a[j].Weight
return w1 > w2
}
For sorting slices, simply use sort.Slice(), added in Go 1.8.
To use sort.Slice() you only need to provide a comparator function, which must tell if an element is less than another.
The logic inside the less() function should test the numbers first, if they differ, the numbers should decide the result. If they are equal, then compare the text values to tell if one is less than the other.
For example:
type Entry struct {
Text string
Number int
}
func main() {
es := []Entry{
{"zaa", 1}, {"aab", 1}, {"xac", 1},
{"zaa", 1}, {"aab", 4}, {"xac", 2},
}
sort.Slice(es, func(i, j int) bool {
if a, b := es[i].Number, es[j].Number; a != b {
return a < b
}
return es[i].Text < es[j].Text
})
fmt.Println(es)
}
Output (try it on the Go Playground):
[{aab 1} {xac 1} {zaa 1} {zaa 1} {xac 2} {aab 4}]
type ByName []WeightBaseResourceInfo
func (a ByName) Len() int { return len(a) }
func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByName) Less(i, j int) bool { return a[i].ResourceId < a[j].ResourceId }
func main() {
resourceWeightInfo := make([]WeightBaseResourceInfo, 3)
start := make([]WeightBaseResourceInfo, 3)
var tempWeightInfo WeightBaseResourceInfo
tempWeightInfo.ResourceId = "zaa"
tempWeightInfo.Weight = 2
resourceWeightInfo[0] = tempWeightInfo
tempWeightInfo.ResourceId = "aab"
tempWeightInfo.Weight = 5
resourceWeightInfo[1] = tempWeightInfo
tempWeightInfo.ResourceId = "xac"
tempWeightInfo.Weight = 1
resourceWeightInfo[2] = tempWeightInfo
copy(start,resourceWeightInfo)
fmt.Println("start", start)
sort.Sort(ByNumericValue(resourceWeightInfo))
if(reflect.DeepEqual(start,resourceWeightInfo)){
sort.Sort(ByName(resourceWeightInfo))
}
fmt.Println("Sorted", resourceWeightInfo)
}
import "reflect"

Getting the first digit of an int

How do I sort a slice of ints by the first digit of each int?
I am trying to write my own custom sort:
type ByFirstDigit []int
func (s ByFirstDigit) Len() int {
return len(s)
}
func (s ByFirstDigit) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s ByFirstDigit) Less(i, j int) bool {
return s[i][0] < s[j][0]
}
But I get this error:
s[j][0] (type int does not support indexing)
#RayfenWindspear has the easiest to use and read answer, but is correct about the performance hit. If performance is more important than maintainability, you can do the same thing using iterative division to get the most-significant base-10 digit:
var i int
for i = n; i >= 10; i = i / 10 {}
// i == most significant digit
Note that you have to declare i outside the loop to be able to use it after the loop finds the most-significant digit. I'd also benchmark both with your own data set to see what the real performance impact is in your particular situation.
Full playground example, courtesy of Rayfen.
You could convert them to strings in your sort function then index the first character. For []int you can use https://godoc.org/strconv#Itoa . Unfortunately this method will take a huge performance hit for the string conversion.
https://play.golang.org/p/S4j3NlfinD
type ByFirstDigit []int
func (s ByFirstDigit) Len() int {
return len(s)
}
func (s ByFirstDigit) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s ByFirstDigit) Less(i, j int) bool {
si := strconv.Itoa(s[i])
sj := strconv.Itoa(s[j])
return si[0] < sj[0]
}
In case you want a solution with a lot of high-level math functions that may or may not perform well:
package main
import "sort"
import "fmt"
import "math"
type ByFirstDigit []int
func (s ByFirstDigit) Len() int {
return len(s)
}
func (s ByFirstDigit) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s ByFirstDigit) Less(i, j int) bool {
return firstDigit( s[i] ) < firstDigit( s[j] )
}
func firstDigit( x int ) int {
return int( math.Abs( float64( x ) ) / math.Pow( 10, float64( numDigits(x) - 1 ) ) )
}
func numDigits( x int ) int {
if ( x == 0 ) { return 1 }
return int( math.Floor( math.Log10( math.Abs( float64(x) ) ) ) ) + 1
}
func main() {
ints := []int{3, 20, 400, -500, 101}
sort.Sort( ByFirstDigit( ints ) )
fmt.Println( ints )
}
Here's a playground (same code):
https://play.golang.org/p/uLyBMlra2N

sorting a uint64 slice in go

I'm writing Go application using Go 1.7rc3.
I have a slice of uint64 (var dirRange []uint64) that I want to sort.
The sort package has a function sort.Ints() but it requires []int and I have []uint64.
What do I do? Can I type cast the all slice?
As of version 1.8, you can use the simpler function sort.Slice. In your case, it would be something like the following:
sort.Slice(dirRange, func(i, j int) bool { return dirRange[i] < dirRange[j] })
This avoids having to define any type just for the sorting.
You can define sort.Interface on your dirRange, which can be a type aliasing []uint64:
type DirRange []uint64
func (a DirRange) Len() int { return len(a) }
func (a DirRange) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a DirRange) Less(i, j int) bool { return a[i] < a[j] }
func main() {
dirRange := DirRange{2, 5, 7, 1, 9, 4}
sort.Sort(dirRange)
fmt.Println(dirRange)
}
Output:
[1 2 4 5 7 9]
This way you can avoid casting and work directly with your array. Since the underlying type is a slice []uint64, you can still use general slice operations. For example:
dirRange := make(DirRange, 10)
dirRange = append(dirRange, 2)
You can provide a type alias for []uint64, add the standard "boilerplate" sorting methods to implement sort.interface (Len, Swap, and Less - https://golang.org/pkg/sort/#Interface); then either create an instance of the new type or typecast an existing slice []uint64 into the new type, as done in the following example (also https://play.golang.org/p/BbB3L9TmBI):
package main
import (
"fmt"
"sort"
)
type uint64arr []uint64
func (a uint64arr) Len() int { return len(a) }
func (a uint64arr) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a uint64arr) Less(i, j int) bool { return a[i] < a[j] }
func (a uint64arr) String() (s string) {
sep := "" // for printing separating commas
for _, el := range a {
s += sep
sep = ", "
s += fmt.Sprintf("%d", el)
}
return
}
func main() {
dirRange := []uint64{3, 2, 400000}
arr := uint64arr(dirRange)
sort.Sort(arr)
fmt.Printf("%s\n", arr)
fmt.Printf("%#v\n", dirRange)
}
The output is:
2, 3, 400000
[]uint64{0x2, 0x3, 0x61a80}
showing that both arrays are sorted since the second one is a typecasted alias for the original.
There is no wrapper function, you need to use the Slice function, and this is an example:
arr := []uint64{5, 0, 3, 2, 1, 6}
sort.Slice(arr, func(i, j int) bool { return arr[i] < arr[j] })

Reverse Slice of strings [duplicate]

This question already has answers here:
How do I reverse a slice in go?
(6 answers)
Closed 11 months ago.
I don't understand what is wrong with the below implementation, I had a look at sort.StringSlice and it looks the same.
type RevStr []string
func(s RevStr) Len() int { return len(s) }
func(s RevStr) Less(i, j int) bool { return s[i] < s[j] }
func(s RevStr) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func Reverse(input string) string {
rs := RevStr(strings.Split(input, " "))
sort.Reverse(rs)
return strings.Join(rs, " ")
}
sort.Reverse doesn't sort the data, but rather returns a new sort.Interface that will sort the data in reverse order. So you don't really need your own type:
func Reverse(input string) string {
s := strings.Split(input, " ")
sort.Sort(sort.Reverse(sort.StringSlice(s)))
return strings.Join(s, " ")
}
Playground: http://play.golang.org/p/w49FDCEHo3.
EDIT: If you just need to reverse a slice of strings, just do:
func reverse(ss []string) {
last := len(ss) - 1
for i := 0; i < len(ss)/2; i++ {
ss[i], ss[last-i] = ss[last-i], ss[i]
}
}
Playground: http://play.golang.org/p/UptIRFV_SI
Nothing is wrong with your RevStr type (though you could just use sort.StringSlice). You're not calling sort.Sort on the reversed implementation:
https://golang.org/pkg/sort/#example_Reverse
package main
import (
"fmt"
"sort"
)
func main() {
s := []int{5, 2, 6, 3, 1, 4} // unsorted
sort.Sort(sort.Reverse(sort.IntSlice(s)))
fmt.Println(s)
}
Although #Ainar-G has provided a way to reverse a slice of strings, I think it's nicer to use two variables in for loop to reverse. But it's only my personal opinion, a matter of style :)
func reverse(s []string) []string {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
Playground link with example of usage: http://play.golang.org/p/v1Cy61NFv1
A one-liner solution (using a lambda):
Given:
myStrings := []string{"apple", "banana", "cherry"}
Sort (in reverse order) with:
sort.Slice(myStrings, func(i, j int) bool { return myStrings[i] > myStrings[j]})
Playground Example:
https://play.golang.org/p/WZabAZTizHG
More simple way, without using built-in sorting feature :
func reverse(s []string) []string {
for i := len(s) - 1; i >= 0; i-- {
result = append(result, s[i])
}
return s
}
func reverseStr(data []string) []string {
m := len(data) - 1
var out = []string{}
for i := m; i >= 0; i-- {
out = append(out, data[i])
}
return out
}

Go sorts slice correctly, but doesn't sort array

I'm puzzled as why this code doesn't work:
package main
import (
"fmt"
"sort"
)
type T [2]int
func (t T) Len() int { return len(t) }
func (t T) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t T) Less(i, j int) bool { return t[i] < t[j] }
func main() {
var x = [2]int{1, 0}
fmt.Println(x)
sort.Sort(T(x))
fmt.Println(x)
}
It outputs (incorrectly):
[1 0]
[1 0]
Changing the type of T to slices does the correct thing.
Slices are inherently reference types, meaning the slice header contains a pointer to the backing array, so they can be mutated without a pointer receiver. Arrays not being a reference type, are copied in full when calling your methods.
In order to do this with an array you need to change everything to use pointers, so your code might look something like:
package main
import (
"fmt"
"sort"
)
type T [2]int
func (t *T) Len() int { return len(t) }
func (t *T) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t *T) Less(i, j int) bool { return t[i] < t[j] }
func main() {
var x = T([2]int{1, 0})
fmt.Println(x)
sort.Sort(&x)
fmt.Println(x)
}

Resources