按键排序哈希,在Ruby中返回哈希

304

这是将哈希表排序并返回哈希对象(而不是数组)的最佳方法吗:

h = {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
# => {"a"=>1, "c"=>3, "b"=>2, "d"=>4}

Hash[h.sort]
# => {"a"=>1, "b"=>2, "c"=>3, "d"=>4}

9
除非您使用eacheach_pair来迭代哈希表,否则我不确定对哈希表进行排序是否有太多优势。即使在这种情况下,我可能仍然会获取键,对其进行排序,然后迭代键并根据需要获取相应的值。这样可以确保代码在旧版本的Ruby上正确运行。 - the Tin Man
是的,我发现您的Hash[h.sort]处理过程比对键进行排序,然后通过排序后的键再次访问哈希表更有效。 - Douglas
"什么是最快的哈希排序方式?将会很有用。" - the Tin Man
6
你已经有几年时间考虑你的解决方案了,现在你准备好接受答案了吗?;-) - Mark Thomas
已经过去了11年,现在让我们接受一个解决方案吧 :P - mlabarca
显示剩余2条评论
12个回答

292

在 Ruby 2.1 中,这很简单:

h.sort.to_h

@zachaysan 但它确实有效:h.sort{|a,z|a<=>z}.to_h(已测试2.1.10、2.3.3)。 - whitehat101
@whitehat101 你说得对。我有一个错误(a是一个数组,而不仅仅是键)。我已经删除了我的评论。 - zachaysan
如果有其他人正在寻找对散列数组进行排序的方法,这个方法可以解决问题(其中 h 为要排序的数组):h.map(&:sort)。map(&:to_h) - J.M. Janzen
1
我不会使用这个解决方案,因为它不是最快的。对于小哈希表来说还可以,但对于成千上万个键而言,它比 h.keys.sort.map { |k| [k, h[k]] }.to_h 慢10倍。不确定在C级别上有何差异,但在基准测试中非常明显。即使是 sort_by(&:first) 也要快得多。 - Kaplan Ilya
@KaplanIlya 可能的区别在于对排序键和整个哈希表进行排序。尽管如此,我更喜欢简单明了的解决方案,除非哈希表足够大以至于这种方法会产生明显的差异,否则我会选择这种方法而不是更复杂的方法。如果是这种情况,排序哈希表可能也不是最佳的架构决策 :) - Mark Thomas
@MarkThomas - 这是如何工作的?它是否按键排序?有参考资料吗? - B Seven

86
注意:Ruby >= 1.9.2有一个保持顺序的哈希表:插入键的顺序将是它们枚举的顺序。以下内容适用于旧版本或向后兼容的代码。
没有排序哈希表的概念。所以,不,你现在做的不对。
如果你想要排序后展示,返回一个字符串:
"{" + h.sort.map{|k,v| "#{k.inspect}=>#{v.inspect}"}.join(", ") + "}"

或者,如果你想按顺序获取键名:

h.keys.sort

或者,如果您想按顺序访问元素:

h.sort.map do |key,value|
  # keys will arrive in order to this block, with their associated value.
end

但总的来说,谈论排序哈希没有意义。从文档中可以了解到:"通过键或值遍历哈希表的顺序可能看起来是随意的,并且通常不会按照插入顺序进行。" 因此,按特定顺序将键插入哈希表并不能起到帮助作用。


27
从1.9.2版本开始,哈希表的插入顺序将会被保留。详情请见http://redmine.ruby-lang.org/issues/show/994。 - David
在最后一个例子中,"h.sort" 就足够了。 - tokland
4
从1.9.2版本开始,哈希插入顺序将被保留。这很好。 - the Tin Man
2
关于我的第一条评论(感觉有点滑稽):例如,依赖哈希排序将在早于1.9.2版本的Ruby中默默地、不可预测地破坏。 - Jo Liss
5
这个回答怎么能够获得20多个点赞,却没有回答OP提出的两个问题之一:"1) OP例子是排序哈希表的最佳方法吗?2)并返回哈希对象"?我不羡慕+1的赞 :) 只是在看完回答后,我仍然无法回答原来的问题。另外,如果重点是不存在排序哈希表这样的东西,请查看此问题的被选答案的评论:http://stackoverflow.com/questions/489139/ruby-1-8-hashsort-not-return-hash-but-array-better-way-to-do-this - Redoman
显示剩余6条评论

72

我一直使用sort_by。你需要用Hash[]来包装#sort_by的输出,以便使其输出哈希表, 否则它会输出一个数组。另外,你也可以在元组的数组上运行#to_h方法,将它们转换为k=>v结构的哈希表。

hsh ={"a" => 1000, "b" => 10, "c" => 200000}
Hash[hsh.sort_by{|k,v| v}] #or hsh.sort_by{|k,v| v}.to_h

在 "如何按数字值对 Ruby 哈希进行排序?" 中有一个类似的问题。


8
在哈希上使用sort_by将返回一个数组。您需要将其映射回哈希表。Hash[hsh.sort_by{|k,v| v}] - stevenspiel
1
是的,枚举器类将哈希值解释为数组。我认为。 - boulder_ruby
4
好的,根据数值排序:hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000} - Cary Swoveland
2
请注意,调用 to_h 仅在 Ruby 2.1.0 及以上版本中受支持。 - Phrogz
1
注释中有一个拼写错误,更正为:sort_by{|k,v| v}.to_h) - jitter
显示剩余3条评论

18

按照 对哈希进行排序,在 Ruby 中返回哈希

使用 解构赋值 和 Hash#sort

hash.sort { |(ak, _), (bk, _)| ak <=> bk }.to_h

Enumerable#sort_by

hash.sort_by { |k, v| k }.to_h

使用默认行为的 Hash#sort 方法

h = { "b" => 2, "c" => 1, "a" => 3  }
h.sort         # e.g. ["a", 20] <=> ["b", 30]
hash.sort.to_h #=> { "a" => 3, "b" => 2, "c" => 1 }

注意: < Ruby 2.1

array = [["key", "value"]] 
hash  = Hash[array]
hash #=> {"key"=>"value"}

注意: > Ruby 2.1

[["key", "value"]].to_h #=> {"key"=>"value"}

3
hash.sort_by { |k, _v| k }.to_h 如果不使用 v,你应该在变量名前加下划线。 - silva96

16

在问题描述中,您给出了最佳的答案:Hash[h.sort] 如果您想要更多可能性,这里提供了使原始哈希在原地进行排序的修改方法:

h.keys.sort.each { |k| h[k] = h.delete k }

1
对于好奇的人来说,在Ruby 1.9.3上,这比https://dev59.com/Cm855IYBdhLWcg3wcz5h#17331221中的“keys sort”略快一些。 - nitrogen

14

不是的,它不支持(Ruby 1.9.x)

require 'benchmark'

h = {"a"=>1, "c"=>3, "b"=>2, "d"=>4}
many = 100_000

Benchmark.bm do |b|
  GC.start

  b.report("hash sort") do
    many.times do
      Hash[h.sort]
    end
  end

  GC.start

  b.report("keys sort") do
    many.times do
      nh = {}
      h.keys.sort.each do |k|
        nh[k] = h[k]
      end
    end
  end
end

       user     system      total        real
hash sort  0.400000   0.000000   0.400000 (  0.405588)
keys sort  0.250000   0.010000   0.260000 (  0.260303)

对于大的哈希值,差异将增加到10倍甚至更多。


6

链接已损坏。 - Snake Sanders
3
我已经修复了链接,使其指向Google,以防一些历史学家想要研究过去是如何完成的。但现在来到这里的任何人都应该使用更新的Ruby版本。 - eremite

2
您可以使用sort方法,然后使用to_h方法将数组转换回哈希表。
h = { "a" => 1, "c" => 3, "b" => 2, "d" => 4 }
h.sort.to_h
# => { "a" => 1, "b" => 2, "c" => 3, "d" => 4 }

0
@ordered = {}
@unordered.keys.sort.each do |key|
  @ordered[key] = @unordered[key]
end

4
如果 Ruby 没有像 Hash#sort_by 这样的方法,你可以按照以下步骤进行操作。 - boulder_ruby

-1

我遇到了同样的问题(需要按照设备名称对我的设备进行排序),我是这样解决的:

<% @equipments.sort.each do |name, quantity| %>
...
<% end %>

@equipments 是我在模型上构建并在控制器上返回的哈希表。如果您调用 .sort,它将根据其键值对哈希表进行排序。


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