Преобразование вложенных ключей хеша из CamelCase в snake_case в Ruby - PullRequest
27 голосов
/ 03 января 2012

Я пытаюсь создать гем обертки API, и возникают проблемы с преобразованием ключей хеш-функции в более Rubyish формат из JSON, который возвращает API.

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"}]}}

Я получаю неправильную рекурсию, и каждая версия такого рода решений, которую я пробовал, либо не преобразует символы за пределы первого уровня, либо выходит за борт и пытается преобразовать весь хэш, включая значения.

Попытка решить все это во вспомогательном классе, вместо того, чтобы по возможности модифицировать реальные функции Hash и String.

Заранее спасибо.

Ответы [ 4 ]

38 голосов
/ 03 января 2012

Вам нужно рассматривать Array и Hash отдельно.И, если вы находитесь в Rails, вы можете использовать underscore вместо своего домашнего напитка 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 к каждому элементу массива;если у вас есть Hash, вы хотите исправить ключи с помощью 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
30 голосов
/ 17 октября 2016

Если вы используете Rails :

Пример с хэшем: 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

Для вложенных атрибутов используйте 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

3 голосов
/ 20 февраля 2014

Принятый ответ «слишком короткий» был превращен в драгоценный камень, плисскен из будущего шимпанзе:

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

Похоже, что он должен работать вне Rails, так как функциональность подчеркивания включена.

0 голосов
/ 24 января 2018

Если вы используете библиотеку active_support, вы можете использовать deep_transform_keys!вот так:

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