How do I get the unique elements from an array of hashes in Ruby?

后端 未结 8 945
抹茶落季
抹茶落季 2020-12-28 14:57

I have an array of hashes, and I want the unique values out of it. Calling Array.uniq doesn\'t give me what I expect.

a = [{:a => 1},{:a =&g         


        
相关标签:
8条回答
  • 2020-12-28 15:17

    Ruby 1.8.7+ will return just what you have expected:

    [{:a=>1}, {:a=>2}, {:a=>1}].uniq
    #=> [{:a=>1}, {:a=>2}] 
    
    0 讨论(0)
  • 2020-12-28 15:18

    Assuming your hashes are always single key-value pairs, this will work:

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

    Hash.to_a creates an array of key-value arrays, so the first map gets you:

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

    uniq on Arrays does what you want, giving you:

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

    and then the second map puts them back together as hashes again.

    0 讨论(0)
  • 2020-12-28 15:19

    You can use (tested in 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}]
    
    0 讨论(0)
  • 2020-12-28 15:21

    The pipe method on arrays (available since 1.8.6) performs set union (returning an array), so the following is another possible way to get unique elements of any array a:

    [] | a

    0 讨论(0)
  • 2020-12-28 15:26

    I've had a similar situation, but hashes had keys. I used sorting method.

    What I mean:

    you have an array:

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

    you sort it (#sort_by {|t| t[:x]}) and get this:

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

    now a bit modified version of answer by Aaaron Hinni:

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

    I've also tried:

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

    but it's very slow. here is my benchmark:

    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
    

    results:

    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)
    
    0 讨论(0)
  • 2020-12-28 15:30

    I can get what I want by calling inject

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

    This will return:

    [{:a=>1}, {:a=>2}]
    
    0 讨论(0)
提交回复
热议问题