只要一个字符串或全部都是符号,sort就会返回我期望的内容。但是有些调用失败了,因为存在混合属性类型的对象(一些符号,一些字符串)。
{"action" => "ok", "test" => "a"}.sort
# => [["action", "ok"], ["test", "a"]]
{:action => "ok", :test => "a"}.sort
# => [[:action, "ok"], [:test, "a"]]
{"action" => "ok", :test => "a"}.sort
# => ArgumentError: comparison of Array with Array failed
这是为什么? Ruby不应该在每个成员上调用to_s
吗?
答案 0 :(得分:6)
sort
在散列上调用to_a
,这使得散列成为数组(键值对)。然后sort
比较元素,这将是数组与数组的比较。
在Array
类中,比较被定义为从一开始就将元素与另一个数组中的对应元素进行比较。
当比较数组中的每个元素时,必须在元素之间定义比较方法。 String
类将其比较定义为另一个String
,但不针对Symbol
。 Symbol
类将其比较定义为另一个Symbol
,但不针对另一个String
。在您的违规案例中,您需要将字符串"action"
与未定义的符号:test
进行比较。为了使其有效,您需要覆盖String
(以及Symbol
的一般性)的比较定义,以定义String
与Symbol
的比较(如以及String
)。一种方法如下:
module PreString
def <=> other; super(other.to_s) end
end
class String; prepend PreString end
module PreSymbol
def <=> other; super(other.to_sym) end
end
class Symbol; prepend PreSymbol end
{"action" => "ok", :test => "a"}.sort
# => [["action", "ok"], [:test, "a"]]
编辑如果您担心创建随机符号(与@SergioTulentsev一样),您可以执行以下操作并忘记上面定义的PreSymbol
:
module Symbol
def <=> other; to_s <=> other end
end
<小时/> Ruby不应该在每个成员上调用
to_s
吗?---编号
答案 1 :(得分:-2)
Ruby的排序是使用<=>
运算符完成的。 <=>
运算符仅适用于相同类型的事物。所以你可以比较两个字符串或两个符号或两个数字等。但你无法比较两个不同的东西。
Ruby不应该to_s
每个事情进行比较。
如果您需要以这种方式进行比较,您可以给出一个块来排序您自己进行转换的位置。
{"action" => "ok", :test => "a"}.sort_by {|a| a[0].to_s}
注意:正如David Grayson在评论中所建议的那样,您可能希望按键对散列进行排序,因此这会进入传递给sort的参数并拉出作为键的第一个元素。