在Ruby中检测重复键的习惯方法?

时间:2011-11-11 00:37:23

标签: ruby hash

我刚刚注意到,如果你向哈希提供重复的密钥,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

但这是否有惯用的方法呢?

7 个答案:

答案 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)

我认为有两种惯用方法可以解决这个问题:

  1. 使用其中一个允许每个键包含多个值的哈希扩展名,或
  2. 扩展散列(或补丁w / flag方法)并实现[]=以抛出欺骗密钥异常。
  3. 您也可以使用引发的[]=装饰现有哈希,或alias_method - 无论哪种方式,它是直截了当的,而且非常漂亮。

答案 3 :(得分:0)

在其他答案中,我已经表明我的观点,Ruby需要一种标准方法来从可枚举构建哈希。所以,正如你需要自己的任务抽象一样,让我们​​用你最喜欢的实现Facets' mashEnumerable#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)

我会避免使用数组来建模哈希。换句话说,不要首先构造对的数组。我不是很滑稽或不屑一顾。我说话的人是多次使用数组和(甚至更糟)平衡数组的人,并且总是后悔。