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.