I have the following method to delete the specified values from a Ruby hash in my Rails application:
我有以下方法从我的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
Something like this works:
像这样的东西有效:
remove_hash_values(some_hash, :some_symbol)
However, this does not work:
但是,这不起作用:
remove_hash_values(some_hash, {})
It works in irb
, which is what confuses me. I'm positive the correct hash is being passed (checked with many puts
statements). My Ruby version is ruby-2.0.0-p247
, and I'm using Rails 3. Any help would be greatly appreciated.
它在irb中工作,这让我很困惑。我很肯定正在传递正确的哈希(使用许多puts语句检查)。我的Ruby版本是ruby-2.0.0-p247,我正在使用Rails 3.任何帮助都将非常感谢。
EDIT: This is not working, either:
编辑:这不起作用,或者:
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
What the hell am I doing wrong?!
我到底做错了什么?!
EDIT 2: Just realised that I'm actually using a HashWithIndifferentAccess
, and not a Hash
. That could be causing some trickery, so I'll try converting it to a Hash
first and reporting back.
编辑2:刚刚意识到我实际上使用的是HashWithIndifferentAccess,而不是哈希。这可能会引起一些诡计,所以我会先尝试将其转换为Hash并报告回来。
3 个解决方案
#1
3
I've tried something similar, e.g.
我尝试过类似的东西,例如
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
Works ok:
工作正常:
2.0.0-p195 :098 > { a: 1, b: 2, c: 3 }.delete_values! 2
=> {:a=>1, :c=>3}
Also works:
还有效:
2.0.0-p195 :101 > { a: 1, b: 2, c: { d: {}, e: 5 } }.delete_values!({})
=> {:a=>1, :b=>2, :c=>{:e=>5}}
Note that this approach won't delete an empty hash if you should make an empty hash further down in the structure--if you had a hash containing only empty hashes--you need to switch the order of the recursion and deletion, e.g.
请注意,如果您应该在结构中进一步向下进行空哈希,则此方法不会删除空哈希 - 如果您的哈希只包含空哈希 - 您需要切换递归和删除的顺序,例如
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
#2
0
(Shown as answer rather than comment to enable display of code.)
(显示为答案而不是评论以启用代码显示。)
Please share your failing test. The following works when run from the Rails 4.0 console with Ruby 2.0p247:
请分享您的失败测试。使用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}
#3
0
Isaac, I like your idea of using recursion, because it deals with hashes nested to any level. I think I understand why your code doesn't work, and how you can fix it. Here's your code:
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
Suppose
假设
hash = {a: 10, b: {c: 10}}
and we invoke:
我们调用:
remove_hash_values(hash, 10)
I expect you want remove_hash_values(hash, 10)
to return {}
, but I believe it returns {b: {}}
. Let's go through the calculations:
我希望你想要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: {}}
Here's how I think you could do it, though I've haven't checked my code. First suppose target
is not a hash
or another structure, just a simple value. Try this:
这是我认为你可以做到的,虽然我还没有检查我的代码。首先假设目标不是另一种结构,只是一个简单的值。尝试这个:
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
If you want to generalize this, so that target
could be a (possibly nested) hash, I think this may work:
如果你想概括这个,那么目标可能是(可能是嵌套的)哈希,我认为这可能有效:
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
#1
3
I've tried something similar, e.g.
我尝试过类似的东西,例如
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
Works ok:
工作正常:
2.0.0-p195 :098 > { a: 1, b: 2, c: 3 }.delete_values! 2
=> {:a=>1, :c=>3}
Also works:
还有效:
2.0.0-p195 :101 > { a: 1, b: 2, c: { d: {}, e: 5 } }.delete_values!({})
=> {:a=>1, :b=>2, :c=>{:e=>5}}
Note that this approach won't delete an empty hash if you should make an empty hash further down in the structure--if you had a hash containing only empty hashes--you need to switch the order of the recursion and deletion, e.g.
请注意,如果您应该在结构中进一步向下进行空哈希,则此方法不会删除空哈希 - 如果您的哈希只包含空哈希 - 您需要切换递归和删除的顺序,例如
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
#2
0
(Shown as answer rather than comment to enable display of code.)
(显示为答案而不是评论以启用代码显示。)
Please share your failing test. The following works when run from the Rails 4.0 console with Ruby 2.0p247:
请分享您的失败测试。使用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}
#3
0
Isaac, I like your idea of using recursion, because it deals with hashes nested to any level. I think I understand why your code doesn't work, and how you can fix it. Here's your code:
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
Suppose
假设
hash = {a: 10, b: {c: 10}}
and we invoke:
我们调用:
remove_hash_values(hash, 10)
I expect you want remove_hash_values(hash, 10)
to return {}
, but I believe it returns {b: {}}
. Let's go through the calculations:
我希望你想要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: {}}
Here's how I think you could do it, though I've haven't checked my code. First suppose target
is not a hash
or another structure, just a simple value. Try this:
这是我认为你可以做到的,虽然我还没有检查我的代码。首先假设目标不是另一种结构,只是一个简单的值。尝试这个:
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
If you want to generalize this, so that target
could be a (possibly nested) hash, I think this may work:
如果你想概括这个,那么目标可能是(可能是嵌套的)哈希,我认为这可能有效:
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