在 Ruby 中将嵌套哈希键从 CamelCase 转换为 Snake_case

发布于 2024-12-24 01:23:02 字数 994 浏览 2 评论 0原文

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

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

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

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

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

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

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

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

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

我的递归错误,并且我遇到过此类解决方案的每个版本尝试要么不转换超出第一级的符号,要么过度尝试转换整个散列,包括值。

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

先感谢您。

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.

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.

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:

def to_snake_case(string)
  string.gsub(/::/, '/').
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
  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.

Thank you in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

你的他你的她 2024-12-31 01:23:02

如果您使用Rails

哈希示例:camelCase 到 Snake_case

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

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

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

对于嵌套属性,请使用 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"}}}

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

If you use Rails:

Example with hash: camelCase to 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

For nested attributes use deep_transform_keys instead of transform_keys, example:

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

简美 2024-12-31 01:23:02

你需要分别对待Array和Hash。而且,如果您使用 Rails,则可以使用 下划线< /code>而不是你的自制 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

如果你的散列有不是符号或字符串的键,那么你可以适当地修改 underscore_key

如果您有一个数组,那么您只想递归地将 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

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

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_key appropriately.

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

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
枯寂 2024-12-31 01:23:02

我使用这个简短的形式:

hash.transform_keys(&:underscore)

并且,正如 @Shanaka Kuruwita 指出的那样,深度转换所有嵌套的哈希值:

hash.deep_transform_keys(&:underscore)

I use this short form:

hash.transform_keys(&:underscore)

And, as @Shanaka Kuruwita pointed out, to deeply transform all the nested hashes:

hash.deep_transform_keys(&:underscore)
混吃等死 2024-12-31 01:23:02

“mu 太短”接受的答案已转换为 gem,futurechimp 的 Plissken:

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

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

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

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.

紫罗兰の梦幻 2024-12-31 01:23:02

使用 deep_transform_keys 进行递归转换。

transform_keys 仅在高级中转换它。

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

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

deep_transform_keys 将更深入并转换所有嵌套的哈希值。

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

hash.deep_transform_keys { |key| key.to_s.underscore }
# => { "camel_case" => "value1", "change_me" => {nested_me: 'value2'} }

Use deep_transform_keys for recursive conversion.

transform_keys only convert it in high level

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

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

deep_transform_keys will go deeper and transform all nested hashes as well.

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

hash.deep_transform_keys { |key| key.to_s.underscore }
# => { "camel_case" => "value1", "change_me" => {nested_me: 'value2'} }
oО清风挽发oО 2024-12-31 01:23:02

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

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

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

hash.deep_transform_keys! do |key|
  k = key.to_s.snakecase rescue key
  k.to_sym rescue key
end
烟酉 2024-12-31 01:23:02

这适用于对象的 camelCasesnake_case 深层嵌套键,这对于 JSON API 非常有用:

def camelize_keys(object)
  deep_transform_keys_in_object!(object) { |key| key.to_s.camelize(:lower) }
end

def snakecase_keys(object)
  deep_transform_keys_in_object!(object) { |key| key.to_s.underscore.to_sym }
end

def deep_transform_keys_in_object!(object, &block)
  case object
  when Hash
    object.keys.each do |key|
      value = object.delete(key)
      object[yield(key)] = deep_transform_keys_in_object!(value, &block)
    end
    object
  when Array
    object.map! { |e| deep_transform_keys_in_object!(e, &block) }
  else
    object
  end
end

This works both to camelCase and snake_case deep nested keys of an object, which is very useful for a JSON API:

def camelize_keys(object)
  deep_transform_keys_in_object!(object) { |key| key.to_s.camelize(:lower) }
end

def snakecase_keys(object)
  deep_transform_keys_in_object!(object) { |key| key.to_s.underscore.to_sym }
end

def deep_transform_keys_in_object!(object, &block)
  case object
  when Hash
    object.keys.each do |key|
      value = object.delete(key)
      object[yield(key)] = deep_transform_keys_in_object!(value, &block)
    end
    object
  when Array
    object.map! { |e| deep_transform_keys_in_object!(e, &block) }
  else
    object
  end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文