alexserver
alexserver

Reputation: 1358

How to create a custom sort method in Ruby

I want to specify a custom block method to sort an object array by evaluating two properties. However, after many searches, I didn't find to any example without the <=> operator.

I want to compare a to b:

if a.x less than b.x return -1
if a.x greater than b.x return 1
if a.x equals b.x, then compare by another property , like a.y vs b.y

This is my code and it doesn't work:

ar.sort! do |a,b|
   if a.x < b.y return -1
   elseif a.x > b.x return 1
   else return a.y <=> b.y
end

This block is within a function return is exiting the function and returning -1.

Upvotes: 19

Views: 25695

Answers (4)

ASP
ASP

Reputation: 1

We can do it with more simple way

ar.sort! do |a,b|
  [a.x, a.y] <=> [b.x, b.y]
end

Or

ar.sort! do |a,b|
  result = a.x <=> b.x
  next if result != 0

  a.y <=> b.y
end

Upvotes: 0

AJcodez
AJcodez

Reputation: 34166

This will give you ascending order for x then for y:

points.sort_by{ |p| [p.x, p.y] }

Upvotes: 33

rossta
rossta

Reputation: 11494

The best answer is provided by @AJcodez below:

points.sort_by{ |p| [p.x, p.y] }

The "correct" answer I originally provided, while it technically works, is not code I would recommend writing. I recall composing my response to fit the question's use of if/else rather than stopping to think or research whether Ruby had a more expressive, succinct way, which, of course, it does.


With a case statement:

ar.sort do |a, b|
  case
  when a.x < b.x
    -1
  when a.x > b.x
    1
  else
    a.y <=> b.y
  end
end 

With ternary:

ar.sort { |a,b| a.x < b.x ? -1 : (a.x > b.x ? 1 : (a.y <=> b.y)) }

Upvotes: 24

fleischer
fleischer

Reputation: 1

This seems to work:

# Ascending
ar.sort! do |a,b|
  (a-b).abs() > 0 ? a-b : 0
end

# Descending
ar.sort! do |a,b|
  (a-b).abs() > 0 ? b-a : 0
end

There's no need for the spaceship operator ( <=> ) - it seems to be superfluous in Ruby (in terms of syntax efficiency), because Ruby considers 0 to be true, and for this usage specifically (above), you can just put the explicit 0 in the ternary operator.

Upvotes: 0

Related Questions