为什么Ruby需要所有类似的属性才能进行排序?

时间:2015-08-03 17:50:17

标签: ruby

只要一个字符串或全部都是符号,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吗?

2 个答案:

答案 0 :(得分:6)

sort在散列上调用to_a,这使得散列成为数组(键值对)。然后sort比较元素,这将是数组与数组的比较。

Array类中,比较被定义为从一开始就将元素与另一个数组中的对应元素进行比较。

当比较数组中的每个元素时,必须在元素之间定义比较方法。 String类将其比较定义为另一个String,但不针对SymbolSymbol类将其比较定义为另一个Symbol,但不针对另一个String。在您的违规案例中,您需要将字符串"action"与未定义的符号:test进行比较。为了使其有效,您需要覆盖String(以及Symbol的一般性)的比较定义,以定义StringSymbol的比较(如以及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的参数并拉出作为键的第一个元素。