迭代嵌套的hash并就地修改值



我正在尝试在不破坏API消费者的情况下‘monkey patch’我们部署的一些代码,基本上我想找到具有键值对的param,其值等于某个值('\u0000'(,并将其设置为零,以下是我所拥有的:

def traverse_param(key, value)
if value.is_a? Hash
value.each do |k, v|
return traverse_param(k, v)
end
end
if value == 'u0000'
value = nil
end
end

在rails 5中,我无法将nil传递给控制器,因为它将被强制转换为'',并且我需要将param中的nil传递给执行以下工作的服务。

现在,由于我可以嵌套params,这就是我检查param值是否为Hash的原因。我使用了这个例子:

params = {:filters=>{:user=>{:disabled_at=>"u0000"}, :importan_param=>1}}
params.each { |k, v| traverse_param(k,v)  }

我正在尝试修改适当的值,但它似乎不起作用?当我调试时,代码进入了正确的位置,但没有产生所需的结果。有什么想法吗?

因此,您面临的问题是修改局部变量value。如果您将整个params散列(或子散列(传递给traverse,则可以更新值如下:

def traverse(params)
params.each do |k, v|
traverse(v) if v.is_a? Hash        
params[k] = nil if v == 'u0000'
end
end
# => {:filters=>{:user=>{:disabled_at=>nil}, :importan_param=>1}}

如果你是Ruby>2.4:,你可以使用transform_values来整理一下

def traverse(params)
params.dup.transform_values { |v| v == "\u0000" ? nil : v.is_a?(Hash) ? traverse(v) : v }
end
traverse(params)
# => {:filters=>{:user=>{:disabled_at=>nil}, :importan_param=>1}}
params
# => {:filters=>{:user=>{:disabled_at=>"\u0000"}, :importan_param=>1}}

或者,可读性更强:

def traverse(params)
params.dup.transform_values do |v| 
next traverse(v) if v.is_a?(Hash)
v == "\u0000" ? nil : v
end
end

希望这是有用的-让我知道你过得怎么样,或者如果你有任何问题。

您正在滥用each进行映射。映射将使用map完成,each用于迭代

def fix(hash)
hash.map do |k, v|
[k, case v
when Hash then fix(v)
when 'u0000' then nil
else v
end]
end.to_h
end

最新更新