我刚刚注意到,如果你向哈希提供重复的密钥,Ruby不会引发异常甚至提供警告:
$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"}
对于key_value_pairs_with_duplicates
,我可以通过
keys = key_value_pairs_with_duplicates.map(&:first)
raise "Duplicate keys" unless keys.uniq == keys
或者通过
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
或者
hash = Hash[key_value_pairs_with_duplicates]
raise "Duplicate keys" unless hash.length == key_value_pairs_with_duplicates.length
但这是否有惯用的方法呢?
答案 0 :(得分:2)
Hash#merge
采用可选块来定义如何处理重复键。
http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-merge
利用此块仅在重复键上调用的事实:
>> 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
答案 1 :(得分:0)
我只是在数组中构建一个哈希,在覆盖一个键之前检查一个值。这样就可以避免创建任何不必要的临时集合。
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
但不,我不认为有一个"惯用的"这样做的方式。它只是遵循规则中的最后一条,如果你不喜欢它,你可以自行解决它。
在字面上,你可能运气不好。但是在字面上,你为什么需要验证这个?如果它是文字的话,你不会从动态来源获得它,所以如果你选择欺骗密钥,那就是你自己的错。只是,呃......不要这样做。
答案 2 :(得分:0)
我认为有两种惯用方法可以解决这个问题:
[]=
以抛出欺骗密钥异常。您也可以使用引发的[]=
装饰现有哈希,或alias_method
- 无论哪种方式,它是直截了当的,而且非常漂亮。
答案 3 :(得分:0)
在其他答案中,我已经表明我的观点,Ruby需要一种标准方法来从可枚举构建哈希。所以,正如你需要自己的任务抽象一样,让我们用你最喜欢的实现Facets' mash(Enumerable#inject
+ Hash#update
看起来对我好)并添加支票:
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
答案 4 :(得分:0)
我认为这里的大多数人都在思考这个问题。要处理重复键,我只需这样做:
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
或者用这个方法制作一个方法,甚至可以用它扩展一个Hash类。或者创建一个Hash类(UniqueHash?)的子代,它默认具有此功能。
但值得吗? (我不这么认为。)我们多久需要像这样处理哈希中的重复键?
答案 5 :(得分:0)
最新的Ruby版本在复制密钥时会提供警告。但是,他们仍然继续将重复项的值重新分配给密钥,这并非总是期望的行为。 IMO,解决此问题的最佳方法是覆盖构造/分配方法。例如。覆盖#[]=
class MyHash < Hash
def []=(key,val)
if self.has_key?(key)
puts("key: #{key} already has a value!")
else
super(key,val)
end
end
end
因此,当您运行时:
h = MyHash.new
h[:A] = ['red']
h[:B] = ['green']
h[:A] = ['blue']
它将输出
key: A already has a value!
{:A=>["red"], :B=>["green"]}
当然,您可以按照自己的方式定制覆盖的行为。
答案 6 :(得分:-1)
我会避免使用数组来建模哈希。换句话说,不要首先构造对的数组。我不是很滑稽或不屑一顾。我说话的人是多次使用数组和(甚至更糟)平衡数组的人,并且总是后悔。