首页 > 解决方案 > 如何在 Ruby 中使用键进行 deep_transform_values?

问题描述

Active Supportdeep_transform_values递归地转换哈希的所有值。但是,是否有类似的方法可以在转换时访问值的键?

我希望能够做到以下几点:

keys_not_to_transform = ['id', 'count']

response = { result: 'ok', errors: [], data: { id: '123', price: '100.0', quotes: ['1.0', '2.0'] }, count: 10 }
response.deep_transform_values! do |key, value|
  # Use value's key to help decide what to do
  return value if keys_not_to_transform.any? key.to_s

  s = value.to_s
  if s.present? && /\A[+-]?\d+(\.\d+)?\z/.match?(s)
    return BigDecimal(s)
  else
    value
  end
end

#Expected result 
# =>{:result=>"ok", :errors=>[], :data=>{:id=>"123", :price=>0.1e3, :quotes=>[0.1e1, 0.2e1]}, :count=>10}

请注意,我们对转换密钥本身不感兴趣,只是在转换相应值时将其放在手边。

标签: rubyhashactivesupport

解决方案


您可以像这样使用Hash#deep_merge!(由 提供ActiveSupport):

keys_not_to_transform = ['id', 'count']

transform_value = lambda do |value|
    s = value.to_s
    if s.present? && /\A[+-]?\d+(\.\d+)?\z/.match?(s)
      BigDecimal(s)
    else
      value
    end
end

transform = Proc.new do |key,value|
  if keys_not_to_transform.include? key.to_s
    value
  elsif value.is_a?(Array)
    value.map! do |v| 
      v.is_a?(Hash) ? v.deep_merge!(v,&transform) : transform_value.(v)
    end 
  else   
    transform_value.(value)
  end  
end

response = { result: 'ok', errors: [], data: { id: '123', price: '100.0', quotes: ['1.0', '2.0'], other: [{id: '124', price: '17.0'}] }, count: 10 }

response.deep_merge!(response, &transform)

这输出:

#=>{:result=>"ok", :errors=>[], :data=>{:id=>"123", :price=>0.1e3, :quotes=>[0.1e1, 0.2e1], :other=>[{:id=>"124", :price=>0.17e2}]}, :count=>10}

推荐阅读