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


257

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

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上正常运行。
Tin Man

在ruby 1.9中也是有意义的。我有一组约会,这些约会按日期分组(作为键),这些日期来自db,我通过ruby手动排序。例如。{“ 2012-09-22”:[...],“ 2012-09-30”:[...],“ 2012-10-12”:[...]}
Adit Saxena 2012年

是的,我发现您的Hash [h.sort]过程比对键排序然后再通过排序的键再次访问哈希更有效。
道格拉斯


3
您已经考虑了几年的解决方案,您准备好接受答案了吗?;-)
Mark Thomas

Answers:


219

在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,2013年

万一有人在寻找一种方式来排序哈希的数组,这会做的伎俩(其中H是数组): h.map(&:sort).map(&:to_h)
JM Janzen

82

注意: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

但总而言之,谈论排序的散列是没有意义的。在docs中,“您通过键或值遍历哈希的顺序似乎是任意的,并且通常不会按插入顺序进行。” 因此,按特定顺序将密钥插入哈希将无济于事。


这是对的。我建议使用RBtree gem获得红宝石中的有序集合功能。
亚伦·斯克鲁格斯

26
1.9.2开始的哈希插入顺序将被保留。参见redmine.ruby-lang.org/issues/show/994
David

4
“将保留开始的1.9.2哈希插入顺序。”,这很不错。
锡人2010年

2
重新发表我的第一条评论(感觉很有趣):例如,对于1.9.2之前的Ruby版本,依赖于哈希排序将无声且不可预测地中断。
乔·里斯

5
在没有回答OP问题的两个部分之一的情况下,这个答案如何得到大约20 +1?“ 1)(OP示例)是否是对哈希排序的最佳方法,2)并返回哈希对象”?我不羡慕+1 :)就在阅读答案之后,我仍然留下了最初的问题。另外,如果要点是没有排序的哈希值,请查看此问题的选定答案的注释stackoverflow.com/questions/489139/…–
jj_

64

我一直都习惯sort_by。您需要包装#sort_by输出,Hash[]以使其输出哈希值,否则输出一个数组数组。另外,要实现此目的,您可以#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 2014年

1
是的,枚举器类将散列解释为数组,我认为
boulder_ruby 2014年

3
正确,排序基于值:hsh.sort_by(&:last).to_h => {"b"=>10, "a"=>1000, "c"=>200000}
卡里·斯沃夫兰

1
请注意,调用to_h在Ruby的2.1.0+只支持
Phrogz

1
评论中有错别字,更正:sort_by{|k,v| v}.to_h)
抖动

13

不,不是(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倍甚至更多



9

按键对哈希排序,在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"}

1
如果不使用v,则应在下划线hash.sort_by { |k, _v| k }.to_h
前面加上



0

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

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

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


-3

我喜欢先前文章中的解决方案。

我做了一个微型班,叫做它class AlphabeticalHash。它还有一个称为的方法ap,该方法接受一个参数a Hash作为输入:ap variable。类似于pp(pp variable

但是它将(尝试并)打印在字母列表(其键)中。Dunno如果有人要使用它,它可以作为gem来使用,您可以这样安装:gem install alphabetical_hash

对我来说,这很简单。如果其他人需要更多功能,请告诉我,我将其包含在gem中。

编辑:感谢Peter,他给了我这个主意。:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.