遍历嵌套哈希并在适当位置修改值

时间:2018-07-31 16:26:51

标签: ruby ruby-on-rails-5

我试图在不破坏API使用方的情况下“猴子补丁”我们已部署的一些代码,基本上我想找到具有键值对的param,其值等于特定值('\ u0000')并将其设置为nil ,这是我所拥有的:

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传递给控制器​​,因为它将被强制转换为'',并且我需要将nil的param传递给执行以下工作的服务。 / p>

现在,因为我可以嵌套参数,所以这就是为什么我检查参数值是否为Hash的原因。我使用了这个示例:

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

我正在尝试修改该值,但是它似乎不起作用?在调试时,代码输入正确的位置,但未产生预期的结果。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

因此,您面临的问题是您正在修改局部变量value。如果您将整个参数哈希(或子哈希)传递给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

希望有用-让我知道您的生活状况或有任何疑问。

答案 1 :(得分:1)

您正在滥用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