randy newfield
randy newfield

Reputation: 1221

How to remove hashes from array based on if all keys in hash match another hash

I have an array of hashes where each hash is a list of URL parameters from URI::decode_www_form. I want to remove duplicates in this array so that all hashes inside the array have unique parameter keys.

For example if I have

arr = [{"update" => "1", "reload" => "true"},
       {"update" => "5", "reload" => "false"},
       {"update" => "9", "reload" => "false"},
       {"update" => "7", "reload" => "true", "newvalue" => "11111"},
       {"page" => "1"}]

I would expect to have an array containing only:

arr = [{"update" => "1", "reload" => "true"},
       {"update" => "7", "reload" => "true", "newvalue" => "11111"},
       {"page" => "1"}]

Where the first three entries are duplicates of each other so only keep one of them, the fourth being unique since it has an extra unique key the first three did not have, and the fifth being unique since it is not the same as any of them.

How would I attempt to solve this problem?

Upvotes: 0

Views: 176

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110755

arr = [{"update" => "1", "reload" => "true"},
       {"update" => "5", "reload" => "false"},
       {"update" => "9", "reload" => "false"},
       {"update" => "7", "reload" => "true", "newvalue" => "11111"},
       {"page" => "1"}]

arr.uniq(&:keys)
  #=> [{"update"=>"1", "reload"=>"true"},
  #    {"update"=>"7", "reload"=>"true", "newvalue"=>"11111"},
  #    {"page"=>"1"}] 

See the doc for Array#uniq for the case where uniq takes a block. In effect, Ruby is doing the following to determine which elements of arr to select:

a = arr.map(&:keys) 
  #=> [["update", "reload"],
  #    ["update", "reload"],
  #    ["update", "reload"],
  #    ["update", "reload", "newvalue"],
  #    ["page"]] 

a.uniq
  #=> [["update", "reload"], ["update", "reload", "newvalue"], ["page"]]

arr.uniq(&:keys) has the same effect as:

arr.uniq { |h| h.keys }
  #=> [{"update"=>"1", "reload"=>"true"},
  #    {"update"=>"7", "reload"=>"true", "newvalue"=>"11111"},
  #    {"page"=>"1"}] 

Many view arr.uniq(&:keys) as just a short-hand way of writing the above expression with the block. That's OK, but actually arr.uniq(&:keys) converts the method (represented by the symbol) :keys to a proc and then calls the proc.

Upvotes: 1

Albin
Albin

Reputation: 3032

You could solve it like this:

tmp = {}
b = arr.select do |h|
  if tmp[h.keys]
    false
  else
    tmp[h.keys] = true
    true
  end
end

Upvotes: 1

Related Questions