Ruby如何按多个值排序?

91

我有一个哈希数组:

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

我想首先按照每个哈希表的“foo”进行排序,然后按照“bar”进行排序。谷歌告诉我应该这样做:

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

但这个代码会让我遇到 "comparison of Array with Array failed" 的 ArgumentError 错误。这是什么意思?


3
可以。你是否在使用旧版的 Ruby?"Works for me." 翻译成中文是 "对我来说可以运作。" - Alex Wayne
2
你发布的内容在1.8.7中有效。 - Phrogz
1
你认为你拥有的数据和实际拥有的数据可能不一样吗? - Wayne Conrad
24
如果用于比较的结果数组同时包含nil和非nil的值,则会引发此异常。 - gucki
请注意,Ruby不能比较布尔值,这也可能导致此错误。 - amoebe
8个回答

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

13
这是同一件事情。Enumerable#sort_by(&block)大致相当于sort { |a,b| block.call(a) <=> block.call(b) },只不过以一种更高效的方式完成。如果这能够运作而sort_by不能,则可能存在其他问题。 - wuputah
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,'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}]
- dj2
不好意思,刚才说的请忽略,我没有注意到你把 sort_by 改成了 sort。但是奇怪的是,a.sort {|a,b| [ a['foo'],a['bar'] ] <=> [b['foo'],b['bar'] ]} 只返回了 ["foo"] - herpderp
6
sort_by 的一个优点是它更符合 DRY 原则。 - Andrew Grimm
显示剩余4条评论

31

这可能意味着您的某个对象中缺少了'foo'或'bar'字段之一。

比较结果类似于nil <=> 2,它返回nil(而不是-101),并且#sort_by不知道如何处理nil

请尝试此操作:

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

16

你发布的代码在 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}] 

4

当用于比较的结果数组同时包含nil值和非nil值时,会出现此异常。


1

Array与Array的比较失败

这意味着(至少在我的情况下),数组元素的类型不同。当我确保所有数组项都是相同类型的(例如 Integer )时,排序开始工作。


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']]

digit不稳定


1
考虑压缩数组(删除 nil 条目),并且如果是字符串比较,则将值小写以进行不区分大小写的排序。
a.compact.sort_by { |h| [h['foo'].downcase, h['bar'].downcase] }

0

优秀的代码。我建议添加处理值为nil的情况。 - phyzalis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接