0

我有以下方法可以从我的 Rails 应用程序中的 Ruby 哈希中删除指定的值:

def remove_hash_values(hash, target = nil)
    hash.delete_if {|key, value| value == target}
    hash.each_value {|obj| remove_hash_values(obj, target) if obj.is_a?(Hash)}
end

像这样的工作:

remove_hash_values(some_hash, :some_symbol)

但是,这不起作用

remove_hash_values(some_hash, {})

它适用于irb,这让我感到困惑。我很肯定正在传递正确的哈希(用许多puts语句检查)。我的 Ruby 版本是ruby-2.0.0-p247,我正在使用 Rails 3。任何帮助将不胜感激。

编辑:这也不起作用:

def remove_hash_values(hash, target = nil)
    hash.each do |key, value|
        hash.delete(key) if value == target
    end
    hash.each_value {|obj| remove_hash_values(obj, target) if obj.is_a?(Hash)}
end

我到底做错了什么?!

编辑 2:刚刚意识到我实际上使用的是 a HashWithIndifferentAccess,而不是 a Hash。这可能会引起一些诡计,所以我会尝试将其转换为Hash第一个并报告回来。

4

3 回答 3

3

我尝试过类似的东西,例如

2.0.0-p195 :092 > class Hash
2.0.0-p195 :093?>   def delete_values! target
2.0.0-p195 :094?>     delete_if { |k, v| v == target }
2.0.0-p195 :095?>     each_value { |v| v.delete_values!(target) if v.is_a?(Hash) }
2.0.0-p195 :096?>   end
2.0.0-p195 :097?> end

工作正常:

2.0.0-p195 :098 > { a: 1, b: 2, c: 3 }.delete_values! 2
 => {:a=>1, :c=>3} 

也有效:

2.0.0-p195 :101 > { a: 1, b: 2, c: { d: {}, e: 5 } }.delete_values!({})
 => {:a=>1, :b=>2, :c=>{:e=>5}} 

请注意,如果您应该在结构中进一步创建一个空散列,则此方法不会删除一个空散列 - 如果您有一个仅包含空散列的散列 - 您需要切换递归和删除的顺序,例如

2.0.0-p195 :092 > class Hash
2.0.0-p195 :093?>   def delete_values! target
2.0.0-p195 :094?>     each_value { |v| v.delete_values!(target) if v.is_a?(Hash) }
2.0.0-p195 :095?>     delete_if { |k, v| v == target }
2.0.0-p195 :096?>   end
2.0.0-p195 :097?> end
于 2013-11-01T00:18:50.007 回答
0

Isaac,我喜欢你使用递归的想法,因为它处理嵌套到任何级别的哈希。我想我理解你的代码为什么不起作用,以及如何修复它。这是你的代码:

def remove_hash_values(hash, target = nil)
  hash.delete_if {|key, value| value == target}
  hash.each_value {|obj| remove_hash_values(obj, target) if obj.is_a?(Hash)}
end

认为

hash = {a: 10, b: {c: 10}}

我们调用:

remove_hash_values(hash, 10)

我期待你想remove_hash_values(hash, 10)回来{},但我相信它会回来{b: {}}。让我们来看看计算:

remove_hash_values(hash, 10)
hash.delete_if {|key, value| value == target}       # hash => {b: {c: 10}} 
hash.each_value {|obj| remove_hash_values(obj, target) if obj.is_a?(Hash)} calls (next line)

  remove_hash_values({c: 10})
  hash.delete_if {|key, value| value == target} # hash => {} 
  hash.each_value {|obj| remove_hash_values(obj, target) if obj.is_a?(Hash)} # hash => {}

hash => {b: {}}

这就是我认为你可以做到的方式,尽管我还没有检查我的代码。首先假设target不是一个hash或另一个结构,只是一个简单的值。尝试这个:

def remove_hash_values(hash, target = nil)
  hash.keys.each do |key|
    value = hash[key]
    case value
    when Hash
      val = remove_hash_values(hash[key], target)  
      if val == {}
        hash.delete(key)
      else
        hash[key] = val
      end
    else
      hash.delete(key) if val == target
    end
  end
  hash
end

如果您想概括这一点,那么这target可能是一个(可能是嵌套的)哈希,我认为这可能有效:

def remove_hash_values(hash, target = nil)
  hash.keys.each do |key|
    value = hash[key]
    if value.is_a? Hash
      if target.is_a? Hash && hash[key] == target
        hash.delete(key)
        next
      end
    else
      # value is not a hash
      hash.delete(key) if !(target.is_a? Hash) && if hash[key] == target)
      next
    end
    # Value is a hash, target may or may not be a hash, value != target
    val = remove_hash_values(hash[key], target)  
    if val == target
      hash.delete(key)
    else    
      hash(key)= val
    end
  end
  hash
end 
于 2013-11-01T03:35:20.273 回答
0

(显示为答案而不是注释以启用代码显示。)

请分享您的失败测试。在使用 Ruby 2.0p247 从 Rails 4.0 控制台运行时,以下内容有效:

def remove_hash_values(hash, target = nil)
  hash.delete_if {|key, value| value == target}
  hash.each_value {|obj| remove_hash_values(obj, target) if obj.is_a?(Hash)}
end

some_hash = HashWithIndifferentAccess.new
some_hash[:foo] = :some_symbol
some_hash[:bar] = {}

remove_hash_values(some_hash, {})

puts some_hash.inspect   # => {"foo"=>:some_symbol}
于 2013-11-01T00:14:38.747 回答