Estoy tratando de construir una API de contenedor joya, y tener problemas con la conversión de los hash de las claves para una más Rubyish formato JSON API devuelve.

El JSON que contiene múltiples capas de anidación, ambos Hashes y Matrices. Lo que quiero hacer es convertir recursivamente todas las claves para snake_case para un uso más fácil.

Aquí es lo que he conseguido 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

El de arriba se 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])/,'_').
  gsub(/([a-z\d])([A-Z])/,'_').
  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"}]}}

Estoy recibiendo la recursividad y el mal, y cada versión de este tipo de solución, he tratado de no convertir los símbolos más allá del primer nivel, o se va por la borda y se intenta convertir todo el hash, incluyendo valores.

Tratando de resolver todo esto en una clase auxiliar, en lugar de modificar el real Hash y funciones de Cadena, si es posible.

Gracias de antemano.

  • Antes de hacer nada, if (not ... and not ...) es un lugar perfecto para el uso De De Morgan de la ley. Usted debe escribir unless ... or ....

4 Comentarios

  1. 39

    Usted necesita para tratar de Matriz y de Hash por separado. Y, si estás en Rails, puede utilizar subrayado en lugar de su homebrew to_snake_case. Primero un poco de ayuda 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 el Hash tiene claves que no son Símbolos o Cadenas, a continuación, puede modificar underscore_key adecuadamente.

    Si usted tiene una Matriz, entonces usted simplemente desea aplicar de forma recursiva convert_hash_keys a cada elemento de la Matriz; si usted tiene un Hash, que quiere arreglar las teclas con underscore_key y aplicar convert_hash_keys a cada uno de los valores; si usted tiene algo más, a continuación, usted desea pasar a través de la virgen:

    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
    • Funciona como un encanto. Gracias por su amabilidad. No voy a hacer esto en Rails, pero creo que el código que he usado para to_snake_case viene de los Rieles underscore método.
    • El string.gsub(/::/, '/') en to_snake_case me hace pensar que estás en lo correcto acerca de dónde to_snake_case vino. De bienvenida para ASÍ disfrutar de su estancia.
    • No es necesario el uso de Rieles, sólo ActiveSupport, que nos permite cherry-pick de la rutina. require 'active_support/core_ext/string/inflections' debe hacer es: 'FooBar'.underscore => "foo_bar".
    • El problema que hay es que estoy requiriendo toda la ActiveSupport joya cuando yo en realidad sólo se necesita un método.
  2. 31

    Si utiliza Rieles:

    Ejemplo con hash: camelCase para 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

    Anidados atributos de uso deep_transform_keys en lugar de transform_keys, 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

    • esto sólo se transforma en el primer nivel del hash, anidada hash de las claves siguen siendo como el camel case.
    • Gracias por la atención que he actualizado mi respuesta.
    • Funciona como un encanto, gracias!
  3. 0

    Si usted está utilizando el active_support de la biblioteca, puede utilizar deep_transform_keys! así:

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

Dejar respuesta

Please enter your comment!
Please enter your name here