Преобразование вложенных хеш-ключей из CamelCase в snake_case в Ruby
Я пытаюсь создать оболочку 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.
Спасибо заранее.
Ответы
Ответ 1
Вам нужно обрабатывать 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
к каждому элементу массива; если у вас есть хэш, вы хотите исправить ключи с помощью 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
Ответ 2
Если вы используете Rails:
Пример с хешем: camelCase to 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
Ответ 3
Принятый ответ "mu is too short" был преобразован в драгоценный камень, futurechimp Plissken:
https://github.com/futurechimp/plissken/blob/master/lib/plissken/ext/hash/to_snake_keys.rb
Похоже, что он должен работать вне Rails, поскольку включена функция подчеркивания.
Ответ 4
Если вы используете библиотеку active_support, вы можете использовать deep_transform_keys! так:
hash.deep_transform_keys! do |key|
k = key.to_s.snakecase rescue key
k.to_sym rescue key
end