Ruby convierte objetos a hash


127

Digamos que tengo un Giftobjeto con @name = "book"& @price = 15.95. ¿Cuál es la mejor manera de convertir eso al Hash {name: "book", price: 15.95}en Ruby, no a Rails (aunque siéntase libre de responder también a los Rails)?


16
¿Lo haría @ gift.attributes.to_options?
Sr. L

1
1) ¿El regalo es un objeto ActiveRecord? 2) ¿podemos suponer que @ name / @ price no son solo variables de instancia sino también lectores de acceso? 3) ¿quieres solo el nombre y el precio o todos los atributos de un regalo, sean los que sean?
tokland

@tokland, 1) no, Giftes exactamente como @nash ha definido , excepto que, 2) seguro, las variables de instancia pueden tener accesores de lector. 3) Todos los atributos en regalo.
ma11hew28

Okay. La pregunta sobre el acceso a las variables de instancia / lectores era saber si se quería un acceso externo (nash) o un método interno (levinalex). Actualicé mi respuesta para el enfoque "interno".
tokland

Respuestas:


80
class Gift
  def initialize
    @name = "book"
    @price = 15.95
  end
end

gift = Gift.new
hash = {}
gift.instance_variables.each {|var| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }
p hash # => {"name"=>"book", "price"=>15.95}

Alternativamente con each_with_object:

gift = Gift.new
hash = gift.instance_variables.each_with_object({}) { |var, hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }
p hash # => {"name"=>"book", "price"=>15.95}

3
Puede usar inyectar para omitir la inicialización de la variable: gift.instance_variables.inject ({}) {| hash, var | hash [var.to_s.delete ("@")] = gift.instance_variable_get (var); hash}
Jordania

8
Agradable. Reemplacé var.to_s.delete("@")con var[1..-1].to_sympara obtener símbolos.
ma11hew28

3
No uses inyectar, usar gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }y deshacerte del final; hash
Narfanator

1
Nunca entenderé el fetiche del rubí each. mapy injectson mucho más poderosos Este es un problema de diseño que tengo con Ruby: mapy que injectse implementa con each. Es simplemente una mala informática.
Nate Symer

Un poco más conciso:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin el


48

Implementar #to_hash?

class Gift
  def to_hash
    hash = {}
    instance_variables.each { |var| hash[var.to_s.delete('@')] = instance_variable_get(var) }
    hash
  end
end


h = Gift.new("Book", 19).to_hash

Técnicamente, debería ser .to_hash, ya que # indica métodos de clase.
Caleb

66
En realidad no. La documentación de RDoc dice: Use :: for describing class methods, # for describing instance methods, and use . for example code(fuente: ruby-doc.org/documentation-guidelines.html ) Además, la documentación oficial (como ruby ​​CHANGELOG, github.com/ruby/ruby/blob/v2_1_0/NEWS ) utiliza #métodos de ejemplo y el punto para métodos de clase de manera bastante consistente.
levinalex

Utilice inyectar en lugar de este antipatrón.
YoTengoUnLCD

Variante de una línea usando each_with_object:instance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
anothermh

43
Gift.new.instance_values # => {"name"=>"book", "price"=>15.95}

10
Esto es Rails, Ruby en sí no tiene instance_values. Tenga en cuenta que Matt pidió una manera de Ruby, específicamente no Rails.
Christopher Creutzig

28
También dijo que siéntase libre de responder a los Rails también ... así que lo hice.
Erik Reedstrom

Que Dios vea ambas versiones aquí;) Me gustó
Sebastian Schürmann

17

Puedes usar el as_jsonmétodo. Convertirá tu objeto en hash.

Pero, ese hash vendrá como un valor para el nombre de ese objeto como una clave. En tu caso,

{'gift' => {'name' => 'book', 'price' => 15.95 }}

Si necesita un hash que está almacenado en el uso del objeto as_json(root: false). Creo que, por defecto, la raíz será falsa. Para obtener más información, consulte la guía oficial de ruby

http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json


13

Para objetos de registro activo

module  ActiveRecordExtension
  def to_hash
    hash = {}; self.attributes.each { |k,v| hash[k] = v }
    return hash
  end
end

class Gift < ActiveRecord::Base
  include ActiveRecordExtension
  ....
end

class Purchase < ActiveRecord::Base
  include ActiveRecordExtension
  ....
end

y luego solo llama

gift.to_hash()
purch.to_hash() 

2
Es curioso que no forme parte del marco de Rails. Parece algo útil tener allí.
Magne

El método de atributos devuelve un nuevo hash con los valores, por lo que no es necesario crear otro en el método to_hash. Así: atributos_nombres.each_with_object ({}) {| name, attrs | attrs [name] = read_attribute (name)}. Ver aquí: github.com/rails/rails/blob/master/activerecord/lib/…
Chris Kimpton

podrías haber hecho esto con el mapa, ¡tu implementación de efectos secundarios está dañando mi mente, hombre!
Nate Symer

11

Si no está en un entorno Rails (es decir, no tiene ActiveRecord disponible), esto puede ser útil:

JSON.parse( object.to_json )

11
class Gift
  def to_hash
    instance_variables.map do |var|
      [var[1..-1].to_sym, instance_variable_get(var)]
    end.to_h
  end
end

6

Puedes escribir una solución muy elegante usando un estilo funcional.

class Object
  def hashify
    Hash[instance_variables.map { |v| [v.to_s[1..-1].to_sym, instance_variable_get v] }]
  end
end

4

Debe anular el inspectmétodo de su objeto para devolver el hash deseado, o simplemente implementar un método similar sin anular el comportamiento predeterminado del objeto.

Si desea ser más elegante, puede iterar sobre las variables de instancia de un objeto con object.instance_variables


4

Convierta recursivamente sus objetos a hash usando gemas 'hashables' ( https://rubygems.org/gems/hashable ) Ejemplo

class A
  include Hashable
  attr_accessor :blist
  def initialize
    @blist = [ B.new(1), { 'b' => B.new(2) } ]
  end
end

class B
  include Hashable
  attr_accessor :id
  def initialize(id); @id = id; end
end

a = A.new
a.to_dh # or a.to_deep_hash
# {:blist=>[{:id=>1}, {"b"=>{:id=>2}}]}

4

Tal vez quiera intentarlo instance_values. Eso funcionó para mí.


1

Produce una copia superficial como un objeto hash de solo los atributos del modelo

my_hash_gift = gift.attributes.dup

Verifique el tipo del objeto resultante

my_hash_gift.class
=> Hash

0

Si necesita convertir objetos anidados también.

# @fn       to_hash obj {{{
# @brief    Convert object to hash
#
# @return   [Hash] Hash representing converted object
#
def to_hash obj
  Hash[obj.instance_variables.map { |key|
    variable = obj.instance_variable_get key
    [key.to_s[1..-1].to_sym,
      if variable.respond_to? <:some_method> then
        hashify variable
      else
        variable
      end
    ]
  }]
end # }}}


0

Para hacer esto sin Rails, una forma limpia es almacenar atributos en una constante.

class Gift
  ATTRIBUTES = [:name, :price]
  attr_accessor(*ATTRIBUTES)
end

Y luego, para convertir una instancia de Gifta Hash, puede:

class Gift
  ...
  def to_h
    ATTRIBUTES.each_with_object({}) do |attribute_name, memo|
      memo[attribute_name] = send(attribute_name)
    end
  end
end

Esta es una buena manera de hacerlo porque solo incluirá lo que defina attr_accessory no todas las variables de instancia.

class Gift
  ATTRIBUTES = [:name, :price]
  attr_accessor(*ATTRIBUTES)

  def create_random_instance_variable
    @xyz = 123
  end

  def to_h
    ATTRIBUTES.each_with_object({}) do |attribute_name, memo|
      memo[attribute_name] = send(attribute_name)
    end
  end
end

g = Gift.new
g.name = "Foo"
g.price = 5.25
g.to_h
#=> {:name=>"Foo", :price=>5.25}

g.create_random_instance_variable
g.to_h
#=> {:name=>"Foo", :price=>5.25}

0

Comencé a usar estructuras para hacer conversiones fáciles de hash. En lugar de utilizar una estructura simple, creo mi propia clase derivada de un hash, esto le permite crear sus propias funciones y documenta las propiedades de una clase.

require 'ostruct'

BaseGift = Struct.new(:name, :price)
class Gift < BaseGift
  def initialize(name, price)
    super(name, price)
  end
  # ... more user defined methods here.
end

g = Gift.new('pearls', 20)
g.to_h # returns: {:name=>"pearls", :price=>20}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.