2013-10-31 167 views
0

比较不工作时,我有以下方法来删除我的Rails应用程序从一个Ruby散列指定值:红宝石哈希“delete_if”空哈希

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:刚才意识到我实际上使用的是HashWithIndifferentAccess,而不是Hash。这可能会导致一些欺骗,所以我会尝试先将它转换为Hash并回报。

+1

我看不出有什么错# 1,但在#2中,您正在迭代散列的元素,这是一个禁忌。为此,请尝试迭代键:'hash.keys.each do | key | hash.delete(key)if hash [key] == target; end'后跟'hash.each_value ...'。 –

回答

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 
+0

谢谢!改变操作顺序(删除与递归)解决了这个问题。仍然试图围绕为什么那样工作... ... –

0

(显示为答案,而不是评论,使代码显示。)

请分享你失败的测试。从Rails 4.0控制台运行Ruby 2.0p247时,以下工作:

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} 
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 

假设

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