I have a list of multidimensional points of type Points.
I've implemented the sort.Sort
interface and can now sort by y value
.
e.g.
type Points []*Point
func (points Points) Len() int {
return len(points)
}
func (points Points) Less(i, j int) bool {
return points[i].y < points[j].y
}
func (points Points) Swap(i, j int) {
points[i], points[j] = points[j], points[i]
}
type Point struct {
x int
y int
country_id int
}
Now I want to sort my points by x value
instead of y value
.
My idea is to use an if statement with a global flag (which can be switched on or off before sorting):
func (points Points) Less(i, j int) bool {
if SORT_BY_X {
return points[i].x < points[j].x
}
return points[i].y < points[j].y
}
Is there a better way of doing this? Should I be implementing Less multiple times? What if I was sorting tables of data by columns for example?
Ah, this is interesting:
sort.Sort()
expects the type to define an ordering and some array operations. You can have "X-sortable point list" and "Y-sortable point list" types, but making them share the array ops works differently from in other languages because Go doesn't use inheritance.The first way I thought of is to create
XSortablePoints
andYSortablePoints
types that each independently implementsort.Interface
, and convert yourPoints
instance to whichever one you need at the moment--see here: http://play.golang.org/p/9V3WlKjOwX.Then nemo had a better way: type embedding allows
XSortablePoints
andYSortablePoints
to share the functions for array operations. Also, nemo doesn't save the sortable types in a variable, which makes sense as they only exist for this one sort call. Here's adjusted sample code: http://play.golang.org/p/wNm-ilM18nNote that neither of these approaches actually copies your point data when you cast, just the slice header. You can see that by looking at the pointer addresses printed out by the first example.
You can get fancier: there's a Points.Sort taking an arbitrary comparison function at http://play.golang.org/p/4PmJVi2_7D. I think the brute-force approach of just defining more types is fine as long as you only have two or three orderings, but situations vary. Note that for types much bigger than the points here, you might want to define the comparer to take pointers rather than values, to avoid copying.
re:
SORT_BY_X
: I'd generally avoid global mode-setting variables that you update as the program runs, because there are lots of ways those can come back to bite you. For example, maybe you'll have two parallel goroutines someday and then trouble happens when they both access the global at once. Or maybe some code will work when the initial value of SORT_BY_X isfalse
, then someday fail because it was lefttrue
after another task ran. If you do find yourself needing a mode variable, figure out if, instead of it being global, you could make it a function parameter or attach it to an object.Finally, there might be a package that already provides some of the higher-level functionality you want. For example, there are some packages related to geographic data listed here: https://code.google.com/p/go-wiki/wiki/Projects#GIS
In addition to user2714852's answer, you can use the same technique as is already used for reversing sorting in the
sort
package: shadowing theLess()
definition.While this is similar to what was already proposed, the way of getting there is a bit different (example on play). You define your points:
And for each sorting method you implement your own type which embeds your
Points
type:Now you can use the different sorting methods like this: