Сортування рубіну за кількома значеннями?


78

У мене є масив хешів:

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

Я хочу сортувати масив спочатку за кожним хешем 'foo', потім за 'bar'. Google повідомляє мені, як це робиться:

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

Але це дає мені ArgumentError "порівняння масиву з масивом не вдалося". Що це означає?


3
Працює для мене. Ви використовуєте стару версію рубіну?
Alex Wayne

2
Те, що ви опублікували, працює в 1.8.7.
Phrogz

1
Чи можливо, що дані, які ви думаєте, що маєте, і дані, які ви насправді маєте, не однакові?
Уейн Конрад,

24
Цей виняток виникає, коли масив результатів, що використовується для порівняння, містить як нульові, так і ненульові значення.
gucki

Також зауважте, що Ruby не може порівнювати булеві значення, що також може спричинити цю помилку.
amoebe

Відповіді:


84
a.sort { |a, b| [a['foo'], a['bar']] <=> [b['foo'], b['bar']] }

11
Це те саме. Enumerable#sort_by(&block)це приблизно sort { |a,b| block.call(a) <=> block.call(b) }, за винятком більш ефективного способу. Якщо це працює, але sort_byні, то щось інше не так.
wuputah

Для простих клавіш сортування є більш ефективним. Для складних ключів sort_by є більш ефективним.
dj2

1
Хм, зараз я отримую "У вас є нульовий предмет, коли ви цього не очікували!" помилка. Точний масив, який я використав, є a=[{'foo'=>0,'bar'=>2},{'foo'=>0,'bar'=>1},{'foo'=>2,'bar'=>1},{'foo'=>1,'bar'=>0}].
herpderp

Мені підходить. >> a = [{'foo' => 0, 'bar' => 2}, {'foo' => 0, 'bar' => 1}, {'foo' => 2, ' бар '=> 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} ]
dj2

6
Однією з переваг sort_by є те, що він більш СУХИЙ.
Ендрю Грімм,

24

Можливо, це означає, що ви пропустили одне з полів „foo“ або „bar“ в одному зі своїх об’єктів.

Порівняння сходить до чого - то , як nil <=> 2, який повертається nil(замість -1, 0або 1) і #sort_byне знає , як поводитися nil.

Спробуйте це:

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

13

Те, що ви опублікували, працює в 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}] 

3

Цей виняток виникає, коли масив результатів, що використовується для порівняння, містить як нульові, так і ненульові значення.


1

Ця помилка з’являється, коли у вас є нестабільні ключі та намагаєтесь за ними сортувати. Приклад:

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

Спробуйте зробити

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

Але це не працює для мене в

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

де цифра нестабільна


1

не вдалося порівняти масив з масивом

Це означає (принаймні в моєму випадку), що типи елементів масиву різні. Коли я переконався, що всі елементи масиву одночасно ( Integerнаприклад), сортування почало працювати.


0

розгляньте можливість ущільнення масиву (видалення нульових записів) і в якості бонусу, якщо це порівняння рядків, зменшіть значення для сортування, що не враховує регістр.

a.compact.sort_by { |h| [h['foo'].downcase, h['bar'].downcase] }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.