25

I have a mulitdimensional array like so:

[
  [name, age, date, gender]
  [name, age, date, gender]
  [..]
]

I'm wondering the best way to sort this array based on multiple conditions...For instance, how would I sort based on age first then by name?

I was messing around with the sort method like so:

array.sort { |a,b| [ a[1], a[0] ] <=> [ b[1], b[0] ] }

Besides that I don't really understand this syntax, I'm not getting the results I would expect. Should I be using the sort method? Should I be individually comparing results by mapping the array?

3

3 Answers 3

51

You should always use sort_by for a keyed sort. Not only is it much more readable, it is also much more efficient. In addition, I would also prefer to use destructuring bind, again, for readability:

array.sort_by {|name, age| [age, name] }
Sign up to request clarification or add additional context in comments.

Comments

17

This should do the trick:

array.sort { |a,b| [ a[1], a[0] ] <=> [ b[1], b[0] ] }

So what does this do? It uses a lot of Ruby idioms.

  • First is blocks, which are sort of like callbacks or anonymous functions/classes in other languages. The sort method of Array uses them to compare two elements based on the return value of the block. You can read all about them here.
  • Next is the <=> operator. It returns -1 if the first argument is less than the second, 0 if they are equal, and 1 if the first is greater than the second. When you use it with arrays, it will compare the arrays element-wise until one of them returns -1 or 1. If the arrays are equal, you will get 0.

1 Comment

You could also use sort_by { |a| [a[1], a[0]] }, constructing all those arrays doesn't come for free.
7

As I understand it you want to order by age first, and then if more than one record has the same age, arrange that subset by name.


This works for me

people = [
      ["bob", 15, "male"], 
      ["alice", 25, "female"], 
      ["bob", 56, "male"], 
      ["dave", 45, "male"], 
      ["alice", 56, "female"], 
      ["adam", 15, "male"]
    ]

people.sort{|a,b| (a[1] <=> b[1]) == 0 ? (a[0] <=> b[0]) : (a[1] <=> b[1]) }

# The sorted array is

[["adam", 15, "male"], 
 ["bob", 15, "male"], 
 ["alice", 25, "female"], 
 ["dave", 45, "male"], 
 ["alice", 56, "female"], 
 ["bob", 56, "male"]]

What this is doing is comparing by age first, and if the age is the same (<=> returs 0) it comparing the name.

3 Comments

very nice! i like that syntax
what if the age is same and then we want to sort the name in descending order?
@Inquisitive then in that case I think you would switch the "a" and the "b" in the "(a[1] <=> b[1])" on the right.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.