An idiomatic way to detect duplicate keys in Ruby?

I just noticed that Ruby does not throw an exception or even not give a warning if you add duplicate keys to the hash:

$VERBOSE = true key_value_pairs_with_duplicates = [[1,"a"], [1, "b"]] # No warning produced Hash[key_value_pairs_with_duplicates] # => {1=>"b"} # Also no warning hash_created_by_literal_with_duplicate_keys = {1 => "a", 1=> "b"} # => {1=>"b"} 

For key_value_pairs_with_duplicates I could detect duplicate keys by doing

 keys = key_value_pairs_with_duplicates.map(&:first) raise "Duplicate keys" unless keys.uniq == keys 

Or by doing

 procedurally_produced_hash = {} key_value_pairs_with_duplicates.each do |key, value| raise "Duplicate key" if procedurally_produced_hash.has_key?(key) procedurally_produced_hash[key] = value end 

or

 hash = Hash[key_value_pairs_with_duplicates] raise "Duplicate keys" unless hash.length == key_value_pairs_with_duplicates.length 

But is there an idiomatic way to do this?

+4
source share
6 answers

Hash#merge accepts an optional block to determine how to handle duplicate keys.

http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-merge

Taking advantage of the fact that this block is called only for duplicate keys:

 >> a = {a: 1, b: 2} => {:a=>1, :b=>2} >> a.merge(c: 3) { |key, old, new| fail "Duplicate key: #{key}" } => {:a=>1, :b=>2, :c=>3} >> a.merge(b: 10, c: 3) { |key, old, new| fail "Duplicate key: #{key}" } RuntimeError: Duplicate key: b 
+2
source

I would just create a hash from the array, checking the value before overwriting the key. Thus, avoid creating unnecessary temporary collections.

 def make_hash(key_value_pairs_with_duplicates) result = {} key_value_pairs_with_duplicates.each do |pair| key, value = pair raise "Duplicate key" if result.has_key?(key) result[key] = value end result end 

But no, I donโ€™t think there is an โ€œidiomaticโ€ way to do this. It just follows the last in the rule, and if you donโ€™t like what you decide, fix it.

Literally, you're probably out of luck. But literally, why do you need to check this? You do not get it from a dynamic source if it is literal, so if you decide to trick the keys, this is your own mistake. Just ... don't do this.

0
source

I think there are two idiomatic ways to handle this:

  • Use one of the Hash extensions that allows multiple values โ€‹โ€‹for each key, or
  • Extend the hash (or the patch w / flag method) and implement []= to throw a dupe key exception.

You can also simply decorate an existing hash with []= , which throws, or alias_method - in any case, straightforwardly and pretty Ruby-ish.

0
source

In other answers, I already stated that Ruby needs a standard method for constructing a hash from an enumerated one. So, since you need your own abstraction for this task, let's just take โ€œFashโ€ for what you like best ( Enumerable#inject + Hash#update looks good to me) and add a check:

 module Enumerable def mash inject({}) do |hash, item| key, value = block_given? ? yield(item) : item fail("Repeated key: #{key}") if hash.has_key?(key) # <- new line hash.update(key => value) end end end 
0
source

I think most people here underestimate the problem. To deal with duplicate keys, I would simply do this:

 arr = [ [:a,1], [:b,2], [:c,3] ] hsh = {} arr.each do |k,v| raise("Whoa! I already have :#{k} key.") if hsh.has_key?(k) x[k] = v end 

Or make a method out of it, maybe even extend its Hash class. Or create a child of the Hash class (UniqueHash?), Which by default would have this functionality.

But is it worth it? (I do not think so). How often do we have to deal with duplicate keys in a hash like this?

0
source

I would not use an array to model a hash at all. In other words, do not create an array of pairs in the first place. I do not despise or neglect. I speak as a person who used arrays of pairs and (even worse) balanced arrays many times and always regretted it.

-1
source

Source: https://habr.com/ru/post/1380680/


All Articles