96

I have an array of hashes:

a=[{ 'foo'=>0,'bar'=>1 },
   { 'foo'=>0,'bar'=>2 },
   ... ]

I want to sort the array first by each hash's 'foo', then by 'bar'. Google tells me this is how it's done:

a.sort_by {|h| [ h['foo'],h['bar'] ]}

But this gives me the ArgumentError "comparison of Array with Array failed". What does this mean?

5
  • 3
    Works for me. Are you using an old version of ruby? Commented Nov 30, 2010 at 1:23
  • 3
    What you have posted works in 1.8.7. Commented Nov 30, 2010 at 4:32
  • 1
    Is it possible that the data you think you have and the data you actually have are not the same? Commented Nov 30, 2010 at 19:35
  • 24
    This exception occurs when the result array used for the comparison contains both nil and non-nil values. Commented Jul 11, 2012 at 21:55
  • Also note that Ruby can not compare boolean values, which might also cause this error. Commented Feb 4, 2019 at 13:46

8 Answers 8

96
a.sort { |a, b| [a['foo'], a['bar']] <=> [b['foo'], b['bar']] }
Sign up to request clarification or add additional context in comments.

9 Comments

This is the same thing. Enumerable#sort_by(&block) is roughly sort { |a,b| block.call(a) <=> block.call(b) }, except done in a more efficient way. If this works but sort_by does not, then something else is amiss.
For simple keys sort is more efficient. For complex keys, sort_by is more efficient.
Hmm, now I'm getting the "You have a nil object when you didn't expect it!" error. The exact array I used is a=[{'foo'=>0,'bar'=>2},{'foo'=>0,'bar'=>1},{'foo'=>2,'bar'=>1},{'foo'=>1,'bar'=>0}].
Works for me.>> a=[{'foo'=>0,'bar'=>2},{'foo'=>0,'bar'=>1},{'foo'=>2,'bar'=>1},{'foo'=>1,'bar'=>0}] => [{"foo"=>0, "bar"=>2}, {"foo"=>0, "bar"=>1}, {"foo"=>2, "bar"=>1}, {"foo"=>1, "bar"=>0}] >> a.sort { |a, b| [a['foo'], a['bar']] <=> [b['foo'], b['bar']] } => [{"foo"=>0, "bar"=>1}, {"foo"=>0, "bar"=>2}, {"foo"=>1, "bar"=>0}, {"foo"=>2, "bar"=>1}]
One advantage of sort_by is that it's more DRY.
|
32

It probably means you're missing one of the fields 'foo' or 'bar' in one of your objects.

The comparison is coming down to something like nil <=> 2, which returns nil (instead of -1, 0 or 1) and #sort_by doesn't know how to handle nil.

Try this:

a.sort_by {|h| [ h['foo'].to_i, h['bar'].to_i ]}

Comments

16

What you have posted works in Ruby 1.8.7:

ruby-1.8.7-p302 > a = [{'foo'=>99,'bar'=>1},{'foo'=>0,'bar'=>2}]
 => [{"foo"=>99, "bar"=>1}, {"foo"=>0, "bar"=>2}] 

ruby-1.8.7-p302 > a.sort_by{ |h| [h['foo'],h['bar']] }
 => [{"foo"=>0, "bar"=>2}, {"foo"=>99, "bar"=>1}] 

ruby-1.8.7-p302 > a.sort_by{ |h| [h['bar'],h['foo']] }
 => [{"foo"=>99, "bar"=>1}, {"foo"=>0, "bar"=>2}] 

Comments

4

This exception occurs when the result array used for the comparison contains both nil and non-nil values.

Comments

3

consider compacting the array (removing nil entries), and as a bonus, if it's string comparision, downcase the values for case insensitive sorting.

a.compact.sort_by { |h| [h['foo'].downcase, h['bar'].downcase] }

Comments

1

This error appeares when you have unstable keys and trying to sort by them. Example:

[{'foo'=>99,'bar'=>1},{'foo'=>0,'bar'=>2, 'qwe' => 7}]
a.sort_by{|v| v['qwe']}
ArgumentError: comparison of NilClass with 7 failed

Try to do

a.sort_by{|v| [v['qwe']].select{|k| not k.nil?}}

But it doesnt work for me in

[v['index'],v['count'],v['digit'],v['value']]

where digit is unstable

Comments

1

comparison of Array with Array failed

This means (at least in my case) that the types of array elements are different. When I made sure all array items are of the same time (Integer, for example), sorting started to work.

Comments

0

I was working on a task where I needed to sort array of hashes by different keys and different directions (by first key ascending, by second descending and etc.)

I've created this helper class, called it AwesomeSort because it's awesome and it's written in Ruby.

https://gist.github.com/pucinsk/36ffef881622b2d9dcca6716f403dc27

1 Comment

Great code. I suggest to add handling for case where a value is nil

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.