0

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?

3
  • How did you attempt to solve this problem? And why do you think that the first three are duplicates? They are all there different. Commented Jul 9, 2016 at 20:00
  • @vgoff Their keys are the same, which is what I am trying to remove duplicates of. Commented Jul 9, 2016 at 20:05
  • You can edit the question to clarify that, rather than entries, to unique keys.. I did not rely on the title giving that hint as strongly as I should have. :( Commented Jul 9, 2016 at 22:19

2 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

2 Comments

Could you add a bit more detail to your answer as to why it works for other readers?
The tmp hash is used to track whether we've seen a specific set of keys before. We iterate over each hash in the array and use its keys to return a value from tmp. If we get nil we haven't seen it before, so we set it to tmp[h.keys] = true and return true. Otherwise we'll just return false. Array#select will return only the values returning true in the supplied block— the first hash with any unique set of keys.
1
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.

1 Comment

Thanks for the answer, can you explain what &: does in this instance?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.