简体   繁体   中英

How to limit an array of similar hashes to those that have more than one of the same key:value pair (details inside)

I have an array like this

arr = [ { name: "Josh", grade: 90 }, {name: "Josh", grade: 70 }, 
{ name: "Kevin", grade: 100 }, { name: "Kevin", grade: 95 }, 
{ name: "Ben", grade: 90 }, { name: "Rod", grade: 90 }, 
{ name: "Rod", grade: 70 }, { name: "Jack", grade: 60 } ]

I would like Ben and Jack to be removed since they only have one record in this array. What would be the most elegant way to get this done? I could manually go through it and check, but is there a better way? Like the opposite of

arr.uniq! { |person| person[:name] }
arr.reject! { |x| arr.count { |y| y[:name] == x[:name] } == 1 }

An O(n) solution:

count_hash = {}
arr.each { |x| count_hash[x[:name]] ||= 0; count_hash[x[:name]] += 1 }
arr.reject! { |x| count_hash[x[:name]] == 1 }

Here are three more ways that might be of some interest, though I prefer Robert's solution.

Each of the following returns:

  #=> [{:name=>"Josh" , :grade=> 90}, {:name=>"Josh" , :grade=>70},
  #    {:name=>"Kevin", :grade=>100}, {:name=>"Kevin", :grade=>95},
  #    {:name=>"Rod"  , :grade=> 90}, {:name=>"Rod"  , :grade=>70}]

#1

Use the well-worn but dependable Enumerable#group_by to aggregate by name, Hash#values to extract the values then reject those that appear but once:

arr.group_by { |h| h[:name] }.values.reject { |a| a.size == 1 }.flatten

#2

Use the class method Hash#new with a default of zero to identify names with multiple entries, then select for those:

multiples = arr.each_with_object(Hash.new(0)) { |h,g| g[h[:name]] += 1 }
               .reject { |_,v| v == 1 } #=> {"Josh"=>2, "Kevin"=>2, "Rod"=>2}
arr.select { |h| multiples.key?(h[:name]) }

#3

Use the form of Hash#update (aka Hash#merge! ) that takes a block to determine names that appear only once, then reject for those:

singles = arr.each_with_object({}) { |h,g|
            g.update({ h[:name] => 1 }) { |_,o,n| o+n } }
             .select { |_,v| v == 1 } #=> {"Ben"=>1, "Jack"=>1}
arr.reject { |h| singles.key?(h[:name]) }  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM