【问题标题】:Sort hash by key, return hash in Ruby按键排序哈希,在 Ruby 中返回哈希
【发布时间】:2011-05-19 09:08:48
【问题描述】:

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

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}

【问题讨论】:

  • 我不确定对哈希进行排序有多大好处,除非您使用 eacheach_pair 对其进行迭代。即使那样,我可能仍然会抓取键,对它们进行排序,然后根据需要遍历它们以获取值。这可确保代码在较旧的 Ruby 上正常运行。
  • 在 ruby​​ 1.9 中也有意义。我有一个约会集合,按日期(作为键)分组,来自 db,我通过 ruby​​ 手动排序。例如。 { "2012-09-22": [...], "2012-09-30": [...], "2012-10-12": [...] }
  • 是的,我发现您的 Hash[h.sort] 过程比排序键然后通过排序键再次访问哈希更有效。
  • 您已经思考了几年的解决方案,您准备好接受答案了吗? ;-)

标签: ruby sorting hashmap


【解决方案1】:

我借用了 Boris Stitnicky 的灵感解决方案,将就地 sort! 方法修补到 Hash

def sort!
  keys.sort!.each { |k| store k, delete(k) }
  self
end

【讨论】:

    【解决方案2】:

    key排序hash,返回Ruby中的hash

    使用解构和Hash#sort

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

    可枚举#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 }
    

    注意:

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

    注意:> Ruby 2.1

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

    【讨论】:

    • 如果不使用v,你应该在前缀hash.sort_by { |k, _v| k }.to_h
    【解决方案3】:

    我遇到了同样的问题(我必须按照他们的名字对我的设备进行分类),我这样解决了:

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

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

    【讨论】:

      【解决方案4】:
      如果您不想使用 ruby​​ 1.9.2 或推出自己的解决方法,

      ActiveSupport::OrderedHash 是另一种选择。

      【讨论】:

      • 链接失效
      • 我修复了指向 Google 的链接,以防某些历史学家想要研究过去如何做到这一点。但是现在任何人都应该使用更新版本的 Ruby。
      【解决方案5】:

      我一直使用sort_by。您需要用Hash[] 包装#sort_by 输出以使其输出哈希,否则它会输出一个数组数组。或者,要完成此操作,您可以在元组数组上运行 #to_h 方法,将它们转换为 k=&gt;v 结构(哈希)。

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

      How to sort a Ruby Hash by number value?”中有类似的问题。

      【讨论】:

      • 在哈希上使用sort_by 将返回一个数组。您需要再次将其映射为哈希。 Hash[hsh.sort_by{|k,v| v}]
      • 是的,我认为枚举器类将哈希解释为数组
      • 对,排序是​​基于值:hsh.sort_by(&amp;:last).to_h =&gt; {"b"=&gt;10, "a"=&gt;1000, "c"=&gt;200000}.
      • 请注意,调用to_h 仅在 Ruby 2.1.0+ 中支持
      • 评论有错别字,更正:sort_by{|k,v| v}.to_h)
      【解决方案6】:

      在 Ruby 2.1 中很简单:

      h.sort.to_h
      

      【讨论】:

      • @zachaysan 但它确实有效:h.sort{|a,z|a&lt;=&gt;z}.to_h(已测试 2.1.10、2.3.3)
      • @whitehat101 你是对的。我有一个错误(a 是一个数组,而不仅仅是键)。我删除了我的评论。
      • 以防万一其他人正在寻找一种方法来对哈希数组进行排序,这将起到作用(其中 h 是数组):h.map(&amp;:sort).map(&amp;:to_h)
      • 我不会使用这个解决方案,因为它不是最快的。对于小散列,这很好,但对于几千个键,它比h.keys.sort.map { |k| [k, h[k]] }.to_h 慢 10 倍。不确定 C 级别的差异在哪里,但在基准测试中非常清楚。甚至sort_by(&amp;:first) 也快得多。
      • @KaplanIlya 可能区别在于对键进行排序与​​对整个哈希进行排序。尽管如此,我还是更喜欢简单和简洁,并且会在更复杂的情况下寻求这个解决方案,除非哈希值足够大并且有明显的差异。如果是这样的话,对哈希进行排序可能不是最好的架构决策:)
      【解决方案7】:

      注意: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 在 ruby​​ 中获得有序集功能。
      • 将保留从 1.9.2 开始的哈希插入顺序。见redmine.ruby-lang.org/issues/show/994
      • “将保留从 1.9.2 开始的哈希插入顺序。”,很甜蜜。
      • 我的第一条评论(感觉很有趣):例如,对于早于 1.9.2 的 Ruby 版本,依赖哈希排序会无声且不可预测地中断。
      • 这个答案是如何得到大约 20 个 +1 的,甚至没有回答 OP 问题的两个部分之一? “1)那(OP示例)是否是对哈希进行排序的最佳方法,2)并返回哈希对象”?我不羡慕+1 :) 就在阅读答案之后,我仍然留下了原始问题。另外,如果重点是没有排序哈希之类的东西,请查看 cmets 以选择此问题的答案stackoverflow.com/questions/489139/…
      【解决方案8】:
      @ordered = {}
      @unordered.keys.sort.each do |key|
        @ordered[key] = @unordered[key]
      end
      

      【讨论】:

      • 如果 ruby​​ 没有像 Hash#sort_by 这样的方法,你会这样做
      【解决方案9】:

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

      【讨论】:

        【解决方案10】:

        你在 OP 中给了自己最好的答案:Hash[h.sort]如果你渴望更多的可能性,这里是对原始哈希的就地修改以使其排序:

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

        【讨论】:

        【解决方案11】:

        我喜欢之前帖子中的解决方案。

        我做了一个迷你班,叫它class AlphabeticalHash。它还有一个名为ap 的方法,它接受一个参数Hash 作为输入:ap variable。类似于 pp (pp variable)

        但它会(尝试并)按字母列表(其键)打印。不知道如果其他人想使用它,它可以作为 gem 使用,你可以这样安装它:gem install alphabetical_hash

        对我来说,这很简单。如果其他人需要更多功能,让 我知道,我会将它包含在 gem 中。

        编辑:感谢Peter,他给了我这个想法。 :)

        【讨论】:

          猜你喜欢
          • 2011-10-25
          • 2010-10-20
          • 2018-11-15
          • 2017-03-24
          • 1970-01-01
          • 2011-07-01
          • 2014-10-27
          • 2011-01-28
          • 2014-01-02
          相关资源
          最近更新 更多