将嵌套的哈希键从 CamelCase 转换为 Ruby 中的 snake_case

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8706930/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:38:50  来源:igfitidea点击:

Converting nested hash keys from CamelCase to snake_case in Ruby

rubyhashkeycamelcasinghash-of-hashes

提问by Andrew Stewart

I'm trying to build an API wrapper gem, and having issues with converting hash keys to a more Rubyish format from the JSON the API returns.

我正在尝试构建一个 API 包装器 gem,并且在将哈希键从 API 返回的 JSON 转换为更像 Rubyish 格式时遇到问题。

The JSON contains multiple layers of nesting, both Hashes and Arrays. What I want to do is to recursively convert all keys to snake_case for easier use.

JSON 包含多层嵌套,包括哈希和数组。我想要做的是递归地将所有键转换为snake_case 以便于使用。

Here's what I've got so far:

这是我到目前为止所得到的:

def convert_hash_keys(value)
  return value if (not value.is_a?(Array) and not value.is_a?(Hash))
  result = value.inject({}) do |new, (key, value)|
    new[to_snake_case(key.to_s).to_sym] = convert_hash_keys(value)
    new
  end
  result
end

The above calls this method to convert strings to snake_case:

上面调用这个方法将字符串转换为snake_case:

def to_snake_case(string)
  string.gsub(/::/, '/').
  gsub(/([A-Z]+)([A-Z][a-z])/,'_').
  gsub(/([a-z\d])([A-Z])/,'_').
  tr("-", "_").
  downcase
end

Ideally, the result would be similar to the following:

理想情况下,结果将类似于以下内容:

hash = {:HashKey => {:NestedHashKey => [{:Key => "value"}]}}

convert_hash_keys(hash)
# => {:hash_key => {:nested_hash_key => [{:key => "value"}]}}

I'm getting the recursion wrong, and every version of this sort of solution I've tried either doesn't convert symbols beyond the first level, or goes overboard and tries to convert the entire hash, including values.

我弄错了递归,我尝试过的这种解决方案的每个版本要么不会转换超出第一级的符号,要么过火并尝试转换整个哈希,包括值。

Trying to solve all this in a helper class, rather than modifying the actual Hash and String functions, if possible.

如果可能,尝试在辅助类中解决所有这些问题,而不是修改实际的 Hash 和 String 函数。

Thank you in advance.

先感谢您。

回答by mu is too short

You need to treat Array and Hash separately. And, if you're in Rails, you can use underscoreinstead of your homebrew to_snake_case. First a little helper to reduce the noise:

您需要分别对待 Array 和 Hash。而且,如果你在 Rails 中,你可以使用underscore代替你的 homebrew to_snake_case。先来个降噪小帮手:

def underscore_key(k)
  k.to_s.underscore.to_sym
  # Or, if you're not in Rails:
  # to_snake_case(k.to_s).to_sym
end

If your Hashes will have keys that aren't Symbols or Strings then you can modify underscore_keyappropriately.

如果您的哈希将具有不是符号或字符串的键,那么您可以进行underscore_key适当的修改。

If you have an Array, then you just want to recursively apply convert_hash_keysto each element of the Array; if you have a Hash, you want to fix the keys with underscore_keyand apply convert_hash_keysto each of the values; if you have something else then you want to pass it through untouched:

如果你有一个数组,那么你只想递归地应用convert_hash_keys到数组的每个元素;如果你有一个哈希,你想用每个值修复键underscore_key并应用于convert_hash_keys每个值;如果你还有别的东西,那么你想通过它原封不动地传递它:

def convert_hash_keys(value)
  case value
    when Array
      value.map { |v| convert_hash_keys(v) }
      # or `value.map(&method(:convert_hash_keys))`
    when Hash
      Hash[value.map { |k, v| [underscore_key(k), convert_hash_keys(v)] }]
    else
      value
   end
end

回答by Hubert Olender

If you use Rails:

如果您使用Rails

Example with hash: camelCase to snake_case:

哈希示例:camelCase 到 snake_case

hash = { camelCase: 'value1', changeMe: 'value2' }

hash.transform_keys { |key| key.to_s.underscore }
# => { "camel_case" => "value1", "change_me" => "value2" }

source: http://apidock.com/rails/v4.0.2/Hash/transform_keys

来源:http: //apidock.com/rails/v4.0.2/Hash/transform_keys

For nested attributes use deep_transform_keys instead of transform_keys, example:

对于嵌套属性,使用 deep_transform_keys 而不是 transform_keys,例如:

hash = { camelCase: 'value1', changeMe: { hereToo: { andMe: 'thanks' } } }

hash.deep_transform_keys { |key| key.to_s.underscore }
# => {"camel_case"=>"value1", "change_me"=>{"here_too"=>{"and_me"=>"thanks"}}}

source: http://apidock.com/rails/v4.2.7/Hash/deep_transform_keys

来源:http: //apidock.com/rails/v4.2.7/Hash/deep_transform_keys

回答by A Fader Darkly

The accepted answer by 'mu is too short' has been converted into a gem, futurechimp's Plissken:

'mu is too short' 接受的答案已转换为宝石,futurechimp 的 Plissken:

https://github.com/futurechimp/plissken/blob/master/lib/plissken/ext/hash/to_snake_keys.rb

https://github.com/futurechimp/plissken/blob/master/lib/plissken/ext/hash/to_snake_keys.rb

This looks like it should work outside of Rails as the underscore functionality is included.

这看起来应该在 Rails 之外工作,因为包含下划线功能。

回答by JayJay

If you're using the active_support library, you can use deep_transform_keys! like so:

如果您使用的是 active_support 库,则可以使用 deep_transform_keys!像这样:

hash.deep_transform_keys! do |key|
  k = key.to_s.snakecase rescue key
  k.to_sym rescue key
end