Ruby convertir un objet en hachage


127

Disons que j'ai un Giftobjet avec @name = "book"& @price = 15.95. Quelle est la meilleure façon de convertir cela en Hash {name: "book", price: 15.95}dans Ruby, pas en Rails (bien que n'hésitez pas à donner la réponse Rails aussi)?


16
Est-ce que @ gift.attributes.to_options ferait l'affaire?
M. L

1
1) Le cadeau est-il un objet ActiveRecord? 2) Pouvons-nous supposer que @ name / @ price ne sont pas seulement des variables d'instance mais aussi des accesseurs de lecteur? 3) Vous ne voulez que le nom et le prix ou tous les attributs d'un cadeau quels qu'ils soient?
tokland

@tokland, 1) non, Giftest exactement comme @nash a défini , sauf, 2) bien sûr, les variables d'instance peuvent avoir des accesseurs de lecture. 3) Tous les attributs du cadeau.
ma11hew28

D'accord. La question sur les variables d'instance / accès des lecteurs était de savoir si on voulait un accès extérieur (nash) ou une méthode intérieure (levinalex). J'ai mis à jour ma réponse pour l'approche «à l'intérieur».
tokland

Réponses:


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}

Alternativement avec 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
Vous pouvez utiliser inject pour ignorer l'initialisation de la variable: gift.instance_variables.inject ({}) {| hash, var | hash [var.to_s.delete ("@")] = gift.instance_variable_get (var); hash}
Jordan

8
Agréable. J'ai remplacé var.to_s.delete("@")par var[1..-1].to_sympour obtenir des symboles.
ma11hew28

3
Ne pas utiliser d'injection, utiliser gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }et se débarrasser de la traîne; hash
Narfanator

1
Je ne comprendrai jamais le fétiche du rubis each. mapet injectsont beaucoup plus puissants. C'est un problème de conception que j'ai avec Ruby: mapet injectsont implémentés avec each. C'est tout simplement une mauvaise informatique.
Nate Symer

Un peu plus concis:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin


48

Mettre en œuvre #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

Techniquement, cela devrait être .to_hash, puisque # indique les méthodes de classe.
Caleb

6
En fait non. La documentation RDoc dit: Use :: for describing class methods, # for describing instance methods, and use . for example code(source: ruby-doc.org/documentation-guidelines.html ) De plus, la documentation officielle (comme le ruby ​​CHANGELOG, github.com/ruby/ruby/blob/v2_1_0/NEWS ) utilise #par exemple des méthodes et le point pour les méthodes de classe de manière assez cohérente.
levinalex

Veuillez utiliser inject au lieu de cet anti-modèle.
YoTengoUnLCD

Variante à une ligne utilisant 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
C'est Rails, Ruby lui-même n'a pas instance_values. Notez que Matt a demandé un moyen Ruby, en particulier pas Rails.
Christopher Creutzig

28
Il a également dit que n'hésitez pas à donner la réponse Rails aussi ... alors je l'ai fait.
Erik Reedstrom

Dieu de voir les deux versions ici;) J'ai aimé
Sebastian Schürmann

17

Vous pouvez utiliser la as_jsonméthode. Cela convertira votre objet en hachage.

Mais, ce hachage viendra en tant que valeur du nom de cet objet en tant que clé. Dans ton cas,

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

Si vous avez besoin d'un hachage stocké dans l'objet, utilisez as_json(root: false). Je pense que la racine par défaut sera fausse. Pour plus d'informations, reportez-vous au guide officiel de rubis

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


13

Pour les objets d'enregistrement actifs

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

et puis juste appeler

gift.to_hash()
purch.to_hash() 

2
drôle ça ne fait pas partie du framework Rails. Cela semble être une chose utile à avoir là-bas.
Magne

La méthode attributes renvoie un nouveau hachage avec les valeurs in - donc pas besoin d'en créer un autre dans la méthode to_hash. Comme ceci: attribut_noms.each_with_object ({}) {| name, attrs | attrs [nom] = read_attribute (nom)}. Voir ici: github.com/rails/rails/blob/master/activerecord/lib/…
Chris Kimpton

vous auriez pu le faire avec map, votre implémentation des effets secondaires me fait mal à l'esprit!
Nate Symer

11

Si vous n'êtes pas dans un environnement Rails (c'est-à-dire que vous n'avez pas ActiveRecord disponible), cela peut être utile:

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

Vous pouvez écrire une solution très élégante en utilisant un style fonctionnel.

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

4

Vous devez remplacer la inspectméthode de votre objet pour renvoyer le hachage souhaité, ou simplement implémenter une méthode similaire sans remplacer le comportement par défaut de l'objet.

Si vous voulez être plus sophistiqué, vous pouvez parcourir les variables d'instance d'un objet avec object.instance_variables


4

Convertissez récursivement vos objets en hachage en utilisant un gem 'hashable' ( https://rubygems.org/gems/hashable ) Exemple

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

Je veux peut-être essayer instance_values. Cela a fonctionné pour moi.


1

Produit une copie superficielle en tant qu'objet de hachage des seuls attributs du modèle

my_hash_gift = gift.attributes.dup

Vérifiez le type de l'objet résultant

my_hash_gift.class
=> Hash

0

Si vous avez également besoin de convertir des objets imbriqués.

# @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

Pour ce faire sans Rails, une méthode propre consiste à stocker les attributs sur une constante.

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

Et puis, pour convertir une instance de Giften a Hash, vous pouvez:

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

C'est un bon moyen de le faire car il n'inclura que ce que vous définissez attr_accessor, et non toutes les variables d'instance.

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

J'ai commencé à utiliser des structures pour faciliter les conversions de hachage. Au lieu d'utiliser une structure nue, je crée ma propre classe dérivée d'un hachage, cela vous permet de créer vos propres fonctions et de documenter les propriétés d'une classe.

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}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.