25

Let's say I have an array of: [{one: 1, two: 2}, {one: 5, two: 6}] and I want to use sort_by something like:

[1] pry(main)> [{one: 1, two: 2}, {one: 5, two: 6}].sort_by{|x| [x[:one], x[:two]]}

However when I introduce nil for one of the values I get ArgumentError: comparison of Array with Array failed :

=> [{:one=>1, :two=>2}, {:one=>5, :two=>6}]
[2] pry(main)> [{one: 1, two: 2}, {one: nil, two: 6}].sort_by{|x| [x[:one], x[:two]]}
ArgumentError: comparison of Array with Array failed

How can I avoid this error?

Eki Eqbal
  • 5,779
  • 9
  • 47
  • 81
  • 2
    in which position should `nil` values appear? if you want to treat them as `0` it is as easy as adding a `.to_i` to the hash access: `x[:one].to_i` (assumption: all values are `nil` or integers) – tessi Feb 21 '16 at 16:45
  • @tessi was thinking just to ignore the value if it's nil as sometimes I need the values to be text or integers – Eki Eqbal Feb 21 '16 at 16:47
  • 1
    What do you mean by "ignore". With `[{one: nil, two: 5},{one: 3, two: 6}]` should the result be `[{one: 3, two: 6}]` ? – tessi Feb 21 '16 at 16:50
  • in real life i have an array of hashes and wanna use it like `array_of_hashes.sort_by{|e| [ e['description'].to_i, e['c_currency'].to_i, e['start_date'], e['maturity_date'] ] }` – Eki Eqbal Feb 21 '16 at 16:50
  • Then I'd do something like `[x[:a_string] || '', x[:some_date] || Date.today, ...`. Thus, adding `|| ` to every hash access. This way you can control at which position `nil` values should appear. – tessi Feb 21 '16 at 16:54
  • @EkiEqbal When you are pulling data from DB, then only you fix it. So that you don't get `nil` value ever. You can do it. – Arup Rakshit Feb 21 '16 at 17:58

1 Answers1

38

Use this syntax,

starred.sort_by { |a| [a ? 1 : 0, a] }

When it has to compare two elements, it compares an arrays. When Ruby compares arrays (calls === method), it compares 1st element, and goes to the 2nd elements only if the 1st are equal. ? 1 : 0 guarantees, that we'll have Fixnum as 1st element, so it should be no error.

If you do ? 0 : 1, nil will appear at the end of array instead of beginning. Here is an example:

irb> [2, 5, 1, nil, 7, 3, nil, nil, 4, 6].sort_by { |i| [i ? 1 : 0, i] }
=> [nil, nil, nil, 1, 2, 3, 4, 5, 6, 7]

Source.

Community
  • 1
  • 1
Albert Paul
  • 1,168
  • 13
  • 23