How to sort an array of struct by field?

9,717 views
Skip to first unread message

Constantine Vasil

unread,
Feb 13, 2013, 1:20:46 AM2/13/13
to golan...@googlegroups.com

I am having these structs:


type Record struct {

          ...

          Distance   string

          ...

          Results    string

}

type Response struct {

        ...

        Version          string

Records          []Record

}


How to sort Response.Records array in ascending order based on Record.Distance?



Kamil Kisiel

unread,
Feb 13, 2013, 1:52:20 AM2/13/13
to golan...@googlegroups.com
I hate to give the "read the documentation" kind of answers, but really, read the documentation :)

This is almost exactly analogous to example given in the sort package: http://golang.org/pkg/sort/#example_Interface

Please have a read and try to understand it, if you're still having problems then ask a more specific question. 

Kyle Lemons

unread,
Feb 13, 2013, 11:16:50 AM2/13/13
to Constantine Vasil, golang-nuts
The general form of any custom sort is essentially the same.

At the call-site:
sort.Sort(byDistance(r.Records))

and by the struct type:
type byDistance []Record
func (v byDistance) Len() int { return len(v) }
func (v byDistance) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
func (v byDistance) Less(i, j int) bool { v[i].Distance < v[j].Distance } 


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Patrick Mylund Nielsen

unread,
Feb 13, 2013, 11:31:31 AM2/13/13
to Kyle Lemons, Constantine Vasil, golang-nuts
This is the best solution.

For a quick and dirty solution (i.e. slower, as it uses reflect), you can do:


sortutil.AscByField(res.Records, "Distance")

Patrick Mylund Nielsen

unread,
Feb 13, 2013, 11:33:47 AM2/13/13
to Kamil Kisiel, golang-nuts
Maybe, but I have to sympathize. It's harder than it should be, and not very intuitive, to e.g. sort by a field or index.


Daniel Bryan

unread,
Feb 13, 2013, 3:55:05 PM2/13/13
to golan...@googlegroups.com
Implementing sort.Interface is fine.. I just wish I could pass the funcs for Less(), Swap() and Len() into the sort function in a pinch, instead of having to go open the file with my types and implement sort semantics for the type that really only apply in one spot.

Kyle Lemons

unread,
Feb 13, 2013, 4:17:19 PM2/13/13
to Daniel Bryan, golang-nuts
On Wed, Feb 13, 2013 at 12:55 PM, Daniel Bryan <danb...@gmail.com> wrote:
Implementing sort.Interface is fine.. I just wish I could pass the funcs for Less(), Swap() and Len() into the sort function in a pinch, instead of having to go open the file with my types and implement sort semantics for the type that really only apply in one spot.

Your instinct is correct. If you need to sort a slice of something in one spot, put the type that implements sort.Interface nearby.  If you find yourself using it again, then move it near the type.

Dan Kortschak

unread,
Feb 13, 2013, 4:17:57 PM2/13/13
to Daniel Bryan, golan...@googlegroups.com
It's not really more complicated than defining a compar func for a C data type and it more clearly shows the association of the comparison to the type.

John Nagle

unread,
Feb 13, 2013, 5:45:04 PM2/13/13
to golan...@googlegroups.com
On 2/13/2013 12:55 PM, Daniel Bryan wrote:
> Implementing sort.Interface is fine.. I just wish I could pass the funcs
> for Less(), Swap() and Len() into the sort function in a pinch, instead of
> having to go open the file with my types and implement sort semantics for
> the type that really only apply in one spot.

What do you do when you need to be able to sort the same
set of structs into different orders? (Use case: directory
listing, which can be sorted by date, type, length, ascending,
descending, etc.)

John Nagle

Daniel Bryan

unread,
Feb 13, 2013, 6:33:35 PM2/13/13
to John Nagle, golan...@googlegroups.com
I tend to have a special struct type that has a swappable attribute for
less. This means its Less method looks like this:

func (p SortableThing) Less(i, j int) bool {
return p.less(i, j)
}


The Sort() method on my main type boxes it in an instance of
SortableThing and then defers to sort.Sort():

func (p MyThing) Sort(sort_by string, less_func func(p MyThing, i, j int) bool) MyThing {
sortable := Sortablething{MyThing, sort_by, less_func}
return MyThing(sort.Sort(sortable))
}

And the final piece in the puzzle is a switch statement on sort_by that
maps it to struct fields.

If someone has a more sane way to do this I'd love to hear it..

David DENG

unread,
Feb 13, 2013, 9:16:41 PM2/13/13
to golan...@googlegroups.com
I just implemented a handy function and put it here:


copy the code or import "github.com/daviddengcn/go-villa" package to use it.

David

Constantine Vasil

unread,
Feb 13, 2013, 10:01:53 PM2/13/13
to golan...@googlegroups.com

Thank you for the link with example but of course I 

was reading it. I was in search for better general solution.


I got it working following the example. It used pointers, my code

do not and I was not sure it will work initially. That is

why I asked here in the forum if there is a better solution.


Having experience, say with VB.NET, some things are

easier to do in VB.NET. Probably Golang could borrow ideas

for better usability.

 

I think Golang solution is low level in a good way and flexible but 

sorting for example is highly un-intuitive. The need to write own 

functions is in-flexible and time consuming. 


For strings Len, Swap and Less should be the same for all String 

related sortings, there is no need to be required to write my own. 

Also for each struct element I need to write a separate sorting function.


The bottom line is that sorting an array of struct by struct element

is very common and should be easier.


I see Golang as very useful language that is gives so much flexibility.

From other side when a project is big there is no time to delve deeper

and one needs ready examples how to do things, There are only three books

on Golang where to search for examples of which only one

"The way to Go" from Ivo Balbaert I am referencing constantly.


Here is the code I got:

/////////////////////////////////////////////////////////////////////////////////

// Sorting ResRecords

/////////////////////////////////////////////////////////////////////////////////

type ResRecords []Record


func (s ResRecords) Len() int {

return len(s)

}


func (s ResRecords) Swap(i, j int) {

s[i], s[j] = s[j], s[i]

}


// ByDistance implements sort.Interface by providing Less and using the Len and

// Swap methods of the embedded ResRecords value.

type ByDistance struct{ ResRecords }


func (s ByDistance) Less(i, j int) bool {

return s.ResRecords[i].Distance < s.ResRecords[j].Distance

}


/////////////////////////////////////////////////////////////////////////////////


Usage:


var response Res


sort.Sort(ByDistance{response.Records})




On Tuesday, February 12, 2013 10:20:46 PM UTC-8, Constantine Vasil wrote:

Constantine Vasil

unread,
Feb 13, 2013, 10:10:37 PM2/13/13
to golan...@googlegroups.com

Just brainstorming. 


If it could be done like this automatically it would be

a dream:

==================================

var response Res

response.Records = sort(Res.Records[0].Record.Distance)
==================================

Referencing the struct element on which to sort on can be something 
like when one is accessing REST or XML elements and
applying a function, in this instance a "sort".

This is just a brainstorming I am not sure if it can be done automatically.

Peter

unread,
Feb 14, 2013, 5:02:21 AM2/14/13
to golan...@googlegroups.com, na...@animats.com
There's a good example of this in the sort package: http://golang.org/pkg/sort/#example_Interface

Basically, it creates types that embed your sortable structure, and implements a different Less() method for each kind of sorting you require.

Hope that makes sense.

Patrick Mylund Nielsen

unread,
Feb 14, 2013, 5:06:36 AM2/14/13
to Constantine Vasil, golang-nuts
It can:

    sortutil.AscByField(res.Records, "Distance")

It's just less efficient.


John Nagle

unread,
Feb 14, 2013, 2:44:41 PM2/14/13
to golan...@googlegroups.com
On 2/14/2013 2:02 AM, Peter wrote:
> There's a good example of this in the sort package:
> http://golang.org/pkg/sort/#example_Interface
>
> Basically, it creates types that embed your sortable structure, and
> implements a different Less() method for each kind of sorting you require.
>
> Hope that makes sense.

That's rather clunky.

A sort function which takes a comparison function with two
Interface parameters would make more sense.

John Nagle



Dan Kortschak

unread,
Feb 14, 2013, 3:31:53 PM2/14/13
to John Nagle, golan...@googlegroups.com
This really depends on your perspective, and your proposal will be slower.

John Asmuth

unread,
Feb 14, 2013, 5:56:58 PM2/14/13
to golan...@googlegroups.com, na...@animats.com
Would the initial ordering come in as an interface{} as well? or perhaps it needs to be moved to a []interface{}? The current approach is quite effective, and sidesteps the issues that arise by not having any generics.

Carlos Castillo

unread,
Feb 14, 2013, 11:44:59 PM2/14/13
to golan...@googlegroups.com, Kyle Lemons, Constantine Vasil
Since everyone seems to be promoting their own packages, let me do the same... :-)

My code (github.com/cookieo9/go-misc/slice) creates a wrapper around an arbitrary slice which provides the Len & Swap methods, and all you need to do is provide the comparator for two elements.


Still, actually implementing an instance of sort.Interface and using sort.Sort is faster and not that big a deal. I barely use this code at all.

Kevin Gillette

unread,
Feb 15, 2013, 12:31:33 AM2/15/13
to golan...@googlegroups.com
I'm not convinced that Kyle Lemon's suggestion, e.g. the canonical, idiomatic approach, is "hard". It's only a trio of very simple, side-effect-free functions, and compared to reflect-based alternatives, is type-safe will usually be faster. Compared to hypothetical meta-programming options, there aren't any unicorns.

Daniel Bryan

unread,
Feb 15, 2013, 1:06:23 AM2/15/13
to Kevin Gillette, golan...@googlegroups.com

The issue for me is more that if you want to sort in different parts of your code on different values you have to jump through a non-idiomatic hoop or have multiple implementations of the interface.

On Feb 15, 2013 4:31 PM, "Kevin Gillette" <extempor...@gmail.com> wrote:
I'm not convinced that Kyle Lemon's suggestion, e.g. the canonical, idiomatic approach, is "hard". It's only a trio of very simple, side-effect-free functions, and compared to reflect-based alternatives, is type-safe will usually be faster. Compared to hypothetical meta-programming options, there aren't any unicorns.

--

Kevin Gillette

unread,
Feb 15, 2013, 2:50:35 AM2/15/13
to golan...@googlegroups.com, Kevin Gillette
Having multiple ways of sorting the same slice actually reduces the code per sort: in particular, only the Less method needs to be redefined (along with a throwaway type) for each sort strategy across the same underlying type. See: <http://play.golang.org/p/rWO6jbev1j>

The amount of code involved is arguably not significantly more than using callback approach.

Joshua Marsh

unread,
Feb 15, 2013, 10:54:56 AM2/15/13
to golan...@googlegroups.com
On Thursday, February 14, 2013 11:06:23 PM UTC-7, Daniel Bryan wrote:

The issue for me is more that if you want to sort in different parts of your code on different values you have to jump through a non-idiomatic hoop or have multiple implementations of the interface.

I had problems with the sort interface at first as well. The problem though was that my mindset was stuck in C. It I were trying to translate code directly from C to Go, I'd be using interface{} and comparison functions. That's not the Go way though. Interfaces and inline structs are the Go equivalent. After changing my mindset, I have to say I enjoy the Go approach much more.
 

John Nagle

unread,
Feb 15, 2013, 3:20:50 PM2/15/13
to golan...@googlegroups.com
On 2/14/2013 10:06 PM, Daniel Bryan wrote:
> The issue for me is more that if you want to sort in different parts of
> your code on different values you have to jump through a non-idiomatic hoop
> or have multiple implementations of the interface.

Here's an simple approach which allows writing a generic
sort where a compare function is passed as a parameter.

http://play.golang.org/p/LyOtZC5Paj

No need for wrapping, or reflection.

However, the line

func (tab dirtab) Index(n int) interface{} { return tab[n] }

implies that an interface object has to be created each
time an element is accessed. If that's done on the stack,
it's not too bad, but if it's done on the heap, there's
a big allocation cost.

John Nagle

Dan Kortschak

unread,
Feb 15, 2013, 3:26:51 PM2/15/13
to John Nagle, golan...@googlegroups.com
It saves one type definition (one line) in the client code at the cost of two type assertions per comparison. Have you looked at what that does to performance?
Reply all
Reply to author
Forward
0 new messages