Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting objects by secondary key when first is equal

Tags:

ruby

How can I sort multiple objects by a secondary key when the first one is equal?

In my Book class I have the following method used for sorting

def <=>(other)
  printed_on <=> other.printed_on
end

Now I need the books printed the same day (printed_on = other.printed_on) to be sorded by page_number.

Since with sort_by you can pass an array of keys, I have tried

 def <=>(other)
   [printed_on <=> other.printed_on, page_number <=> other.page_number]
 end

but I get

undefined method `>' for [1, 1]:Array

like image 724
Sig Avatar asked Nov 25 '25 02:11

Sig


1 Answers

Arrays are compared lexicographically (bold emphasis mine):

Arrays are compared in an “element-wise” manner; the first element of ary is compared with the first one of other_ary using the <=> operator, then each of the second elements, etc… As soon as the result of any such comparison is non zero (i.e. the two corresponding elements are not equal), that result is returned for the whole array comparison.

[This is just a convoluted way of saying "Arrays are compared lexicographically".]

So you could simply use an Array for your comparison:

def <=>(other)
  [printed_on, page_number] <=> [other.printed_on, other.page_number]
end

Your method violates the contract of <=> (bold emphasis mine):

Your implementation of #<=> should return one of the following values: -1, 0, 1 or nil.

There are only four allowed return values for a conforming implementation of <=>:

  • -1: less-than
  • 0: equal
  • +1: greater-than
  • nil: uncomparable (partially ordered)

You are returning neither of those four, you are returning an Array. As always, if you break the contract, strange things can happen.

like image 178
Jörg W Mittag Avatar answered Nov 27 '25 15:11

Jörg W Mittag