首页 > 解决方案 > 迭代嵌套哈希并就地修改值

问题描述

我正在尝试在不破坏 API 使用者的情况下对我们部署的一些代码进行“猴子补丁”,基本上我想找到具有键值对的参数,其值等于某个值('\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给控制器​​,因为它将被转换为'',我需要将nilin 参数传递给执行以下工作的服务。

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

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

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

标签: rubyruby-on-rails-5

解决方案


因此,您面临的问题是您正在修改一个局部变量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}}

transform_values如果你是 Ruby > 2.4,你可以稍微整理一下:

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

希望这很有用 - 如果您有任何问题,请告诉我您的进展情况。


推荐阅读