【问题标题】:How do I get the unique elements from an array of hashes in Ruby?如何从 Ruby 中的哈希数组中获取唯一元素?
【发布时间】:2010-09-15 21:59:32
【问题描述】:

我有一个哈希数组,我想要其中的唯一值。致电Array.uniq 并没有达到我的预期。

a = [{:a => 1},{:a => 2}, {:a => 1}]
a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}]

我所期望的:

[{:a => 1}, {:a => 2}]

在网上四处搜索时,我没有找到令我满意的解决方案。人们建议重新定义Hash.eql?Hash.hash,因为这是Array.uniq 正在查询的内容。

编辑: 我在现实世界中遇到这种情况时,哈希值稍微复杂一些。它们是具有多个字段的解析 JSON 的结果,其中一些值也是哈希值。我有一组想要过滤掉唯一值的结果。

我不喜欢重新定义Hash.eql?Hash.hash 解决方案,因为我要么必须全局重新定义Hash,要么为数组中的每个条目重新定义它。为每个条目更改 Hash 的定义会很麻烦,尤其是因为每个条目内部可能有嵌套的哈希值。

在全球范围内更改Hash 具有一定的潜力,尤其是在临时完成的情况下。我想构建另一个类或辅助函数来包装保存旧定义并恢复它们,但我认为这增加了比实际需要更多的复杂性。

使用inject 似乎是重新定义Hash 的好选择。

【问题讨论】:

    标签: ruby arrays hash unique


    【解决方案1】:

    我可以通过拨打inject得到我想要的东西

    a = [{:a => 1},{:a => 2}, {:a => 1}]
    a.inject([]) { |result,h| result << h unless result.include?(h); result }
    

    这将返回:

    [{:a=>1}, {:a=>2}]
    

    【讨论】:

    • 我认为比我上面发布的一个链接要好得多
    【解决方案2】:

    您给出的答案与here 讨论的答案相似。它会覆盖要出现在数组中的哈希值上的 hasheql? 方法,然后使 uniq 行为正确。

    【讨论】:

    • 这是我在网上找到的解决方案之一。我不喜欢我需要重新定义哈希,只是为了调用 uniq。
    • 如果普通的 Hash 和 Array 类不能满足您的需求,您应该真正考虑定义您自己的类来实现所需的行为。你能描述一下你试图用哈希数组建模什么吗?
    【解决方案3】:

    假设您的哈希始终是单个键值对,这将起作用:

    a.map {|h| h.to_a[0]}.uniq.map {|k,v| {k => v}}
    

    Hash.to_a 创建一个键值数组的数组,所以第一个 map 得到你:

    [[:a, 1], [:a, 2], [:a, 1]]
    

    uniq on Arrays 做你想做的事,给你:

    [[:a, 1], [:a, 2]]
    

    然后第二张地图再次将它们重新组合为哈希。

    【讨论】:

    • 我遇到的现实问题使用了更复杂的哈希。
    【解决方案4】:
    【解决方案5】:

    我也遇到过类似的情况,但哈希有键。我使用了排序方法。

    我的意思:

    你有一个数组:

    [{:x=>1},{:x=>2},{:x=>3},{:x=>2},{:x=>1}]
    

    你对它进行排序 (#sort_by {|t| t[:x]}) 并得到这个:

    [{:x=>1}, {:x=>1}, {:x=>2}, {:x=>2}, {:x=>3}]
    

    现在 Aaaron Hinni 的回答稍作修改:

    your_array.inject([]) do |result,item| 
      result << item if !result.last||result.last[:x]!=item[:x]
      result
    end
    

    我也试过了:

    test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]}
    

    但是速度很慢。这是我的基准:

    test=[]
    1000.times {test<<{:x=>rand}}
    
    Benchmark.bmbm do |bm|
      bm.report("sorting: ") do
        test.sort_by {|t| t[:x]}.inject([]) {|r,h| r<<h if !r.last||r.last[:x]!=h[:x]; r}
      end
      bm.report("inject: ") {test.inject([]) {|r,h| r<<h unless r.find {|t| t[:x]==h[:x]}; r}.sort_by {|t| t[:x]} }
    end
    

    结果:

    Rehearsal ---------------------------------------------
    sorting:    0.010000   0.000000   0.010000 (  0.005633)
    inject:     0.470000   0.140000   0.610000 (  0.621973)
    ------------------------------------ total: 0.620000sec
    
                    user     system      total        real
    sorting:    0.010000   0.000000   0.010000 (  0.003839)
    inject:     0.480000   0.130000   0.610000 (  0.612438)
    

    【讨论】:

      【解决方案6】:

      Ruby 1.8.7+ 将返回您所期望的结果:

      [{:a=>1}, {:a=>2}, {:a=>1}].uniq
      #=> [{:a=>1}, {:a=>2}] 
      

      【讨论】:

        【解决方案7】:

        数组上的 pipe 方法(自 1.8.6 起可用)执行集合并集(返回数组),因此以下是获取任何数组的唯一元素的另一种可能方法a

        [] | a

        【讨论】:

        • @SunnyRGupta,你用的是什么版本的 Ruby?
        • ruby 1.9.3p448 (2013-06-27 revision 41675) [x86_64-darwin13.2.0]
        • @SunnyRGupta,我不知道该说什么。我刚刚再次测试,它工作得很好。 $ irb 1.9.3p448 :001 > a = [{:a => 1},{:a => 2}, {:a => 1}] => [{:a=>1}, {:a= >2}, {:a=>1}] 1.9.3p448:002 > [] | a => [{:a=>1}, {:a=>2}]
        【解决方案8】:

        您可以使用(在 ruby​​ 1.9.3 中测试),

        [{a: 1},{a: 2},{a:1}].uniq => [{a:1},{a: 2}]
        [{a: 1,b: 2},{a: 2, b: 2},{a: 1, b: 3}].uniq_by {|v| v[:a]} => [{a: 1,b: 2},{a: 2, b: 2}]
        

        【讨论】:

          猜你喜欢
          • 2021-09-11
          • 2018-02-04
          • 1970-01-01
          • 2012-06-21
          • 1970-01-01
          • 2021-08-19
          • 1970-01-01
          • 1970-01-01
          • 2021-07-29
          相关资源
          最近更新 更多