如何按键按字母顺序对Ruby Hash进行排序

时间:2014-12-09 17:22:41

标签: ruby sorting

我正在尝试按键按字母顺序对哈希进行排序,但是如果不创建自己的排序类,我似乎无法找到方法。我发现下面的代码按值排序,如果它是一个整数,我试图修改它但没有任何运气。

temp["ninjas"]=36
temp["pirates"]=12
temp["cheese"]=222
temp.sort_by { |key, val| key }

我的目标是按键排序哈希,然后输出值。我将不得不使用不同的哈希顺序多次执行此操作,但值相同。

5 个答案:

答案 0 :(得分:29)

假设您希望输出是一个散列,它将按排序顺序迭代键,那么您就在那里。 Hash#sort_by返回ArrayArray,内部数组都是两个元素。

Ruby的Hash有一个可以使用此输出的构造函数。

试试这个:

temp = Hash[ temp.sort_by { |key, val| key } ]

或更简洁

temp = temp.sort_by { |key| key }.to_h

如果您的哈希具有混合键类型,这将不起作用(例如,Ruby不会在StringSymbol之间自动排序),您将收到类似比较的错误消息带字符串的符号失败(ArgumentError)。如果是这样,您可以将上述内容改为

temp = Hash[ temp.sort_by { |key, val| key.to_s } ] 

解决这个问题。但是请注意,密钥仍将保留其原始类型,这可能会导致后续代码中的假设出现问题。此外,大多数内置类都支持.to_s方法,因此您可能会从中获得不需要的结果(例如数字键或其他意外类型的意外排序顺序)。

您可以另外,将密钥转换为Strings,如下所示:

temp = Hash[ temp.map { |key, val| [key.to_s, val] }.sort ] 

。 。 。虽然这种方法会丢失有关原始密钥类型的信息,但却无法可靠地回溯原始数据。

答案 1 :(得分:7)

sorted_by_key = Hash[original_hash.sort]

将通过按键按字母顺序插入original_hash的键/值来创建新的哈希值。 Ruby 2.x哈希记住它们的插入顺序,因此如果你枚举它或输出它,这个新哈希将按键排序。

如果以非字母顺序插入更多元素,当然不会这样。

此外,这假设原始哈希键都是可排序/可比较的。

答案 2 :(得分:4)

Ruby的Hash现在记得它的插入顺序,但早期的Rubies< v1.9不要。但是,不要打扰哈希,因为这样做没有任何优势,因为哈希基本上是一个随机访问结构。这意味着这些元素在任何时候都可以访问,无论是第一个还是最后一个元素都不会产生影响,您可以同样访问它们。

这与数组不同,数组的作用类似于顺序/文本文件或链或队列,您必须通过迭代它来线性访问它,此时,元素的顺序使得很大的不同。

因此,使用Hash,获取密钥,对它们进行排序,然后迭代密钥列表或使用values_at一次检索所有值。例如:

hash = {
    'z' => 9,
    'a' => 1
}

sorted_keys = hash.keys.sort # => ["a", "z"]
sorted_keys.each do |k|
  puts hash[k]
end
# >> 1
# >> 9

hash.values_at(*sorted_keys) # => [1, 9]

有些语言甚至不会让你对哈希进行排序,并且通过排序的键列表访问它是提取顺序中元素的唯一方法,所以它可能不是一个好主意。养成依赖键/值对顺序的习惯,而不是依赖键。

答案 3 :(得分:0)

您可以创建一个新的空哈希来保存已排序的哈希数据。迭代返回的数组并将数据加载到新哈希中以保存已排序的哈希数据。

temp = {}
temp["ninjas"]=36
temp["pirates"]=12
temp["cheese"]=222 
temp = temp.sort_by { |key, val| key }

temp_sorted = {}
temp.each { |sub_arr| temp_sorted[sub_arr[0]] = sub_arr[1] } 
temp = temp_sorted

temp现在等于{“cheese”=> 222,“ninjas”=> 36,“pirates”=> 12}

答案 4 :(得分:0)

除了Neil Slater's answer之外,它还使用Hash#sort_by方法(在块中输出可比较的值时非常简洁)...

irb(main):001:0> h = { a: 0, b: 5, c: 3, d: 2, e: 3, f:1 }
=> {:a=>0, :b=>5, :c=>3, :d=>2, :e=>3, :f=>1}
irb(main):002:0> h.sort_by { |pair| pair[1] }.to_h
=> {:a=>0, :f=>1, :d=>2, :c=>3, :e=>3, :b=>5}

...或相反的版本...

irb(main):003:0> h.sort_by { |pair| pair[1] }.reverse.to_h
=> {:b=>5, :e=>3, :c=>3, :d=>2, :f=>1, :a=>0}

...还有一种使用Array#sort方法的选项,该方法允许您定义自己的比较规则(例如,按值升序排序,然后按相等值的键降序排序):

irb(main):004:0> h.to_a.sort { |one, other| (one[1] == other[1]) ? other[0] <=> one[0] : one[1] <=> other[1] }.to_h
=> {:a=>0, :f=>1, :d=>2, :e=>3, :c=>3, :b=>5}

最后一个选项不太简洁,但可以更灵活(例如,用于处理类型混合的自定义逻辑)。