Conversión de claves hash anidadas de CamelCase a snake_case en Ruby
key camelcasing (4)
Intento crear una gema envoltura de API y tener problemas para convertir las claves hash a un formato más Rubyish del JSON que devuelve la API.
El JSON contiene múltiples capas de anidamiento, tanto Hashes como Arrays. Lo que quiero hacer es convertir recursivamente todas las claves a snake_case para facilitar su uso.
Esto es lo que tengo hasta ahora:
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
Lo anterior llama a este método para convertir cadenas a 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
Idealmente, el resultado sería similar al siguiente:
hash = {:HashKey => {:NestedHashKey => [{:Key => "value"}]}}
convert_hash_keys(hash)
# => {:hash_key => {:nested_hash_key => [{:key => "value"}]}}
Me está equivocando la recursión, y cada versión de este tipo de solución que he probado no convierte los símbolos más allá del primer nivel, o va por la borda e intenta convertir todo el hash, incluidos los valores.
Intentando resolver todo esto en una clase de ayuda, en lugar de modificar las funciones reales de Hash y String, si es posible.
Gracias de antemano.
Debe tratar Array y Hash por separado. Y, si estás en Rails, puedes usar un underscore
lugar de tu homebrew to_snake_case
. Primero un pequeño ayudante para reducir el ruido:
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
Si tus Hashes tendrán claves que no son Símbolos o Cadenas, entonces puedes modificar underscore_key
apropiadamente.
Si tienes una matriz, entonces solo quieres aplicar recursivamente convert_hash_keys
a cada elemento de la matriz; si tiene un Hash, quiere arreglar las claves con underscore_key
y aplicar convert_hash_keys
a cada uno de los valores; si tienes algo más, entonces quieres pasarlo intacto:
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
La respuesta aceptada por "mu es demasiado corta" se ha convertido en una gema, Plissken de futurechimp:
https://github.com/futurechimp/plissken/blob/master/lib/plissken/ext/hash/to_snake_keys.rb
Parece que debería funcionar fuera de Rails ya que incluye la funcionalidad de subrayado.
Si está utilizando la biblioteca active_support, puede usar deep_transform_keys! al igual que:
hash.deep_transform_keys! do |key|
k = key.to_s.snakecase rescue key
k.to_sym rescue key
end
Si usa Rails :
Ejemplo con hash: camelCase a snake_case :
hash = { camelCase: ''value1'', changeMe: ''value2'' }
hash.transform_keys { |key| key.to_s.underscore }
# => { "camel_case" => "value1", "change_me" => "value2" }
fuente: http://apidock.com/rails/v4.0.2/Hash/transform_keys
Para los atributos anidados, use deep_transform_keys en lugar de transform_keys, por ejemplo:
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"}}}
fuente: http://apidock.com/rails/v4.2.7/Hash/deep_transform_keys