I do have
4, 5', 6, 5''
and want to reverse stable sort as
6, 5', 5'', 4
but not
6, 5'', 5', 4
This (invalid) code would not work
keys := []int{4, 5', 6, 5''}
sort.Stable(sort.Reverse(sort.Ints(keys)))
it would produce:
6, 5'', 5', 4
Here the problem is shown as simplified as a slice of integers, but In reality I need to use it applied to a slice of structs
type myStruct struct {
t time.Time
d time.Duration
}
and reverse stable sort based in the t field.
Edit: After few comments I made explicit that the integer one is a non working example to simplify the problem.
Implement the sort.Interface interface on a slice type, so you can choose the sort order, and apply a stable sort on it. Example : https://play.golang.org/p/TWAtH7asi3
Implement the sort.Interface interface on your custom struct.
type myStruct struct{
t time.Time
d time.Duration
}
type Slice []myStruct
func (s Slice) Len() int { return len(s) }
func (s Slice) Less(i, j int) bool {
return (s[i].t).After(s[j].t)
}
func (s Slice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
In your case, following function will sort in reverse order based on t
func (s Slice) Less(i, j int) bool {
return (s[i].t).After(s[j].t)
}
(s[i].t).After(s[j].t) reports whether s[i].t is after s[j].t.
If you want only sort, use following one
func (s Slice) Less(i, j int) bool {
return (s[i].t).Before(s[j].t)
}
Hope this will help.
It seems you don't need to go through the trouble of implementing the sort interface. You can just sort bare using sort.Slice or sort.SliceStable.
Here is what worked for me (go playground):
package main
import (
"fmt"
"sort"
"time"
)
func main() {
layout := "Jan 2 15:04:05 -0700 MST 2006"
t1, _ := time.Parse(layout, "Jan 2 15:04:05 -0700 MST 2008")
t2, _ := time.Parse(layout, "Jan 2 15:04:05 -0700 MST 2001")
t3, _ := time.Parse(layout, "Jan 2 15:04:05 -0700 MST 2003")
t4, _ := time.Parse(layout, "Jan 2 15:04:05 -0700 MST 2006")
timestamps := []struct {
T time.Time
d time.Duration
}{
{t1, 1},
{t2, 1},
{t3, 1},
{t4, 1},
}
// forward
sort.Slice(timestamps, func(i, j int) bool {
return timestamps[i].T.Before(timestamps[j].T)
})
fmt.Println("By time:", timestamps)
// reverse
sort.Slice(timestamps, func(i, j int) bool {
return timestamps[i].T.After(timestamps[j].T)
})
fmt.Println("By time:", timestamps)
}
Related
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] })
I need to sort a slice of a type that is coming from a 3rdparty package. Based on some condition the order must be ascending or descending.
The solution I come up with is:
type fooAscending []foo
func (v fooAscending) Len() int { return len(v) }
func (v fooAscending) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
func (v fooAscending) Less(i, j int) bool { return v[i].Amount < v[j].Amount }
type fooDescending []foo
func (v fooDescending) Len() int { return len(v) }
func (v fooDescending) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
func (v fooDescending) Less(i, j int) bool { return v[i].Amount > v[j].Amount }
if someCondition {
sort.Sort(fooAscending(array))
} else {
sort.Sort(fooDescending(array))
}
Is there a better way to do this. 13 lines of code for this task and most of it is duplicated, seems a bit too much.
As of Go 1.8, there is an easier way to sort a slice that does not require you to define new types. You simply pass an anonymous function to the sort.Slice function.
a := []int{5, 3, 4, 7, 8, 9}
sort.Slice(a, func(i, j int) bool {
return a[i] < a[j]
})
for _, v := range a {
fmt.Println(v)
}
This will sort in ascending order, if you want the opposite, simply write a[i] > a[j] in the anonymous function.
You're looking for sort.Reverse. That will let you say:
sort.Sort(sort.Reverse(fooAscending(s)))
My answer below is based on the assumption that the slice that you are receiving from a third party package is of a basic Go type.
To sort slices of basic types, use the sort package utilities. Here is an example that sorts a slice of string and a slice of int.
package main
import (
"fmt"
"sort"
)
func main() {
sl := []string{"mumbai", "london", "tokyo", "seattle"}
sort.Sort(sort.StringSlice(sl))
fmt.Println(sl)
intSlice := []int{3,5,6,4,2,293,-34}
sort.Sort(sort.IntSlice(intSlice))
fmt.Println(intSlice)
}
The output of the above is:
[london mumbai seattle tokyo]
[-34 2 3 4 5 6 293]
Go to Go Playground here to try it out yourself.
A few things of note:
Sorting basic Go types does not require implementing functions such as Len() that belong to sort.Interface. You need to take that route only for composite types.
Just wrap the type of a basic type using an appropriate Interface method provider, e.g. StringSlice, IntSlice, or Float64Slice, and sort.
The slice is sorted in-place, and hence does not return a copy of the sorted slice.
you can import the "sort" package from standard library of golang . then you can use either "Slice" or "SliceStable" function to sort your slice. it is recommended to use the second one like this :
sort.SliceStable(yourSlice , anonnymousFunction)
example :
package main
import (
"fmt"
"sort"
)
func main() {
a := []int{4,5,9,6,8,3,5,7,99,58,1}
sort.SliceStable(a, func(i,j int )bool{
//i,j are represented for two value of the slice .
return a[i] < a[j]
})
fmt.Println(a)
}
The accepted answer is good, but I disagree with their suggestion on descending:
a[i] > a[j]
With sort.Slice, the provided function is supposed to represent an
implementation of "less than":
func Slice(x interface{}, less func(i, j int) bool)
Slice sorts the slice x given the provided less function. It panics if x is not a slice.
So writing a "greater than" function, isn't really true to the given description. Better would be to reverse the indexes:
package main
import (
"fmt"
"sort"
)
func main() {
a := []int{5, 3, 4, 7, 8, 9}
sort.Slice(a, func(i, j int) bool {
return a[j] < a[i]
})
fmt.Println(a) // [9 8 7 5 4 3]
}
both should return the same result, but I think one is more idiomatic.
https://golang.org/pkg/sort#Slice
maybe you could use sort.Sort method to sort slice. :)
func TestSorted(t *testing.T) {
nums := []int{4, 3, 2, 3, 5, 2, 1}
// descending
sort.Sort(sort.Reverse(sort.IntSlice(nums)))
fmt.Println(nums) // [5 4 3 3 2 2 1]
// ascending
sort.Sort(sort.IntSlice(nums))
fmt.Println(nums) // [1 2 2 3 3 4 5]
}
var names = []string{"b", "a", "e", "c", "d"}
sort.Strings(names)
fmt.Println("Sorted in alphabetical order", names)
sort.Sort(sort.Reverse(sort.StringSlice(names)))
fmt.Println("Sorted in reverse order", names)
link for The Go Playgound https://play.golang.org/p/Q8KY_JE__kx
if for any reasons you can't or don't want to use the sort package, the following will implement a bubble sort type of sorting (it accepts an int64 slice and returns an int64 slice):
func sortSlice ( S []int64 ) []int64 {
// sort using bubblesort, comparing each pairs of numbers and ensuring that left is lower than right
for i := len(S); i > 0 ; i-- {
for j := 1; j < i; j++ {
if S[j-1] > S[j] {
// swap
intermediate := S[j]
S[j] = S[j-1]
S[j-1] = intermediate
}
}
}
return S
}
I know we can use
sort.Sort(sort.Reverse(sort.IntSlice(example)))
to sort a array.
But how can I get the indices of the array?
e.g.
example := []int{1, 25, 3, 5, 4}
I want to get the output: 1, 3, 5, 4, 2
Make a wrapper for sort.IntSlice that remembers the indexes and swaps them when it swaps the values:
type Slice struct {
sort.IntSlice
idx []int
}
func (s Slice) Swap(i, j int) {
s.IntSlice.Swap(i, j)
s.idx[i], s.idx[j] = s.idx[j], s.idx[i]
}
Playground: http://play.golang.org/p/LnSLfe-fXk.
EDIT: As DaveC mentioned in the comments, you can actually wrap around sort.Interface to create a data structure for any sortable type:
type Slice struct {
sort.Interface
idx []int
}
func (s Slice) Swap(i, j int) {
s.Interface.Swap(i, j)
s.idx[i], s.idx[j] = s.idx[j], s.idx[i]
}
The accepted answer is good! But I needed a version of it that "didn't touch" my slice.
You can do it like this:
type sortable struct {
nums, idxs []int
}
func (s sortable) Len() int { return len(s.nums) }
func (s sortable) Less(i, j int) bool { return s.nums[i] < s.nums[j] }
func (s sortable) Swap(i, j int) {
s.nums[i], s.nums[j] = s.nums[j], s.nums[i]
s.idxs[i], s.idxs[j] = s.idxs[j], s.idxs[i]
}
func sortAndReturnIdxs(nums []int) []int {
idxs := make([]int, len(nums))
for i := range idxs {
idxs[i] = i
}
sort.Sort(sortable{nums, idxs})
return idxs
}
And then you can just call the function on your slice:
func main() {
nums := []int{4, 1, 6}
fmt.Println(sortAndReturnIdxs(nums)) // [1 0 2]
fmt.Println(nums) // [1 4 6]
}
The function returns the indices, and sorts the array in place.
Playground link: https://go.dev/play/p/7tli8tNeWNt
You can also just get the indices and avoid mutating the slice in-place, see https://github.com/mkmik/argsort
Seems a basic question, but can't find a simple answer.
I have a slice:
[]string{"dog", "cat", "bird"}
What's the best way to sort it by looking up the sorting values in a map:
map[string]int{"dog": 2, "cat":3, "bird": 1}
So that the slice is ordered as below:
[]string{"bird", "dog", "cat"}
Implement the sort.Interface interface for a type that stores the data and the weights:
import "sort"
type WeightedStringSlice struct {
Strings []string
Weights map[string]int
}
func (s *WeightedStringSlice) Len() int {
return len(s.Strings)
}
func (s *WeightedStringSlice) Less(i, j int) bool {
return s.Weights[s.Strings[i]] < s.Weights[s.Strings[j]]
}
func (s *WeightedStringSlice) Swap(i, j int) {
s.Strings[i], s.Strings[j] = s.Strings[j], s.Strings[i]
}
Then call sort.Sort on it:
data := WeightedStringSlice{
Strings: []string{"dog", "cat", "bird"},
Weights: map[string]int{"dog": 2, "cat": 3, "bird": 1},
}
sort.Sort(&data)
fmt.Printf("%v\n", data.Strings)
Live Demo
I have a map of structs that I am populating by streaming data to a Go program. The way the map is updated is similar to the example below.
Once I have this map of structs populated, what is the best (or good) way to sort this map by the values of the count field in the struct?
package main
type data struct {
count int64
}
func main() {
m := make(map[string]data)
m["x"] = data{0, 0}
if xx, ok := m["x"]; ok {
xx.count = 2
m["x"] = xx
} else {
panic("X isn't in the map")
}
}
This example can be run here: http://play.golang.org/p/OawL6QIXuO
As siritinga already pointed out, the elements of a map isn't ordered, so you cannot sort it.
What you can do is to create a slice and sort the elements using the sort package:
package main
import (
"fmt"
"sort"
)
type dataSlice []*data
type data struct {
count int64
size int64
}
// Len is part of sort.Interface.
func (d dataSlice) Len() int {
return len(d)
}
// Swap is part of sort.Interface.
func (d dataSlice) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
// Less is part of sort.Interface. We use count as the value to sort by
func (d dataSlice) Less(i, j int) bool {
return d[i].count < d[j].count
}
func main() {
m := map[string]*data {
"x": {0, 0},
"y": {2, 9},
"z": {1, 7},
}
s := make(dataSlice, 0, len(m))
for _, d := range m {
s = append(s, d)
}
// We just add 3 to one of our structs
d := m["x"]
d.count += 3
sort.Sort(s)
for _, d := range s {
fmt.Printf("%+v\n", *d)
}
}
Output:
{count:1 size:7}
{count:2 size:9}
{count:3 size:0}
Playground
Edit
Updated the example to use pointers and to include a map so that you can both do lookups and have a slice to sort over.