2013-10-31 170 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