37

I'm performing this query in a controler and the 'Group' model has_many Users

@group= Group.find(params[:id])

@group is being used to render this partial (the partial dumps the users of a group into a table)

<%= render :partial=>"user_list", :locals=>{:users=>@group.users} %>

The local variable 'users' passed to the partial is an array of User objects;

- !ruby/object:User 
  attributes: 
    updated_at: 2011-01-04 21:12:04
    firstname:  Bob
    lastname: Smith
    id: "15"
    group_id: "2"
    created_at: 2010-11-26 12:54:45

How can the user array be sorted by 'lastname'? I've tried several different ways without any luck. Trying to sort by a object attribute inside an array in confusing me. Also, I don't undertand how I could do this with an :order in the query (how to :order not the Group but the Users of each group)?

Maybe I'm not referring to name of the object correctly ('User')? It seems like this should work be it produces a 'no method' error (or a dynamic constant assignment error if 'sort_by' is used without the !):

 users.sort_by! {|User| User.lastname}

Thanks for any help.

Reno
  • 2,962
  • 9
  • 41
  • 68

4 Answers4

42

I found a method that works from here. I don't understand what the "&" symbol is doing - maybe it's shorthad for "object" since in my case ":lastname" is an attribute of the object that make up the array.

users = users.sort_by &:lastname

note: I don't undertand why but a destructive version won't work. It produces a "undefined method `sort_by!' for #" errror:

users.sort_by! &:lastname
Community
  • 1
  • 1
Reno
  • 2,962
  • 9
  • 41
  • 68
  • 5
    "&" is short-hand for the "to_proc" method. On symbols, to_proc essentially converts it into calling that method by name on whatever is passed in. A short explanation: http://pragdave.pragprog.com/pragdave/2005/11/symbolto_proc.html (note that that's pretty old, it's built-in in 1.9 and up, IIRC, and is added by Rails in older versions) – Groxx Nov 05 '13 at 20:54
  • the destructive method doesn't work here because users is an active record relation, not an array. there is considerable overlap in terms of the methods that exist on both, but not complete. and you wouldn't want to be able to use destructive methods on active record relations. – ian root Aug 13 '20 at 20:22
32

See http://ariejan.net/2007/01/28/ruby-sort-an-array-of-objects-by-an-attribute/

@users.sort! { |a,b| a.name.downcase <=> b.name.downcase }
BvuRVKyUVlViVIc7
  • 11,641
  • 9
  • 59
  • 111
  • Thanks, I got a "The error occurred while evaluating nil.sort!" error (I changed 'name' to 'lastname'). It seems like that example doesn't take into consideration that 'lastname' is an attribute of the User model (where users is an array of User objects). – Reno Jan 06 '11 at 02:40
  • if your error is "nil.sort!", the problem is the receiver of sort i.e. @users is nil. Solve that first. – Aditya Sanghi Jan 06 '11 at 07:35
  • Thanks. @users (or in my case 'users') isn't nil. I'm pretty sure because the example above is for objects of an array and not attributes inside those array objects. This method did work (where I believe '&' stands for 'object): users = users.sort_by &:lastname – Reno Jan 06 '11 at 13:22
17

It's probably because you've got |User| when User is a class. Try changing it to an un-used variable name, like u, to get:

 users.sort_by! {|u| u.lastname}

The term between the pipes is the name of the variable each item is stored in, as it runs through, not the type of object you've got. This is the same as other Ruby blocks, like do || end, or any other {||}-style block.

Groxx
  • 2,489
  • 1
  • 25
  • 32
  • Thanks, that didn't work though. It returned a "undefined method `sort_by!' for # " eror – Reno Jan 06 '11 at 02:26
  • Sorry about that ^^; Not sure I can suggest anything more, it's been a while since I've been in the Ruby/Rails world. Glad to see you got something working, though! – Groxx Jan 07 '11 at 22:08
  • @reno You got an undefined method `sort_by!` because you're trying to sort an object. `sort!` will work only on a collection of objects (e.g. array of Objects). – adarsh Jun 08 '13 at 16:19
2

You can try putting an order clause in the "has_many :users" line that you have in your Group model

Aditya Sanghi
  • 13,370
  • 2
  • 44
  • 50