Ruby konvertiert Objekt in Hash

127

Angenommen, ich habe ein GiftObjekt mit @name = "book"& @price = 15.95. Was ist der beste Weg, um dies {name: "book", price: 15.95}in Ruby in Hash umzuwandeln , nicht in Rails (obwohl Sie auch gerne die Antwort von Rails geben können)?

ma11hew28
quelle
16
Würde @ gift.attributes.to_options tun?
Herr L
1
1) Ist Geschenk ein ActiveRecord-Objekt? 2) Können wir annehmen, dass @ name / @ price nicht nur Instanzvariablen, sondern auch Reader Accessors sind? 3) Sie möchten nur Name und Preis oder alle Attribute in einem Geschenk, was auch immer sie sind?
Tokand
@tokland, 1) nein, Giftist genau so, wie @nash es definiert hat , außer 2) sicher, dass die Instanzvariablen Reader-Accessoren haben können. 3) Alle Attribute im Geschenk.
ma11hew28
OK. Die Frage zum Zugriff auf Instanzvariablen / Leser war, ob ein externer Zugriff (nash) oder eine interne Methode (levinalex) gewünscht wird. Ich habe meine Antwort für den "Inside" -Ansatz aktualisiert.
Tokand

Antworten:

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}

Alternativ mit 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}
Vasiliy Ermolovich
quelle
3
Mit inj können Sie die Initialisierung der Variablen überspringen: gift.instance_variables.inject ({}) {| hash, var | hash [var.to_s.delete ("@")] = gift.instance_variable_get (var); Hash}
Jordanien
8
Nett. Ich ersetzte var.to_s.delete("@")mit var[1..-1].to_symSymbolen zu erhalten.
ma11hew28
3
Verwenden Sie nicht injizieren, verwenden Sie gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }und ; hash
entfernen Sie
1
Ich werde den Rubinfetisch nie verstehen each. mapund injectsind viel mächtiger. Dies ist ein Designproblem, das ich mit Ruby habe: mapund injectmit dem ich implementiert bin each. Es ist einfach schlechte Informatik.
Nate Symer
Etwas prägnanter:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin
293

Sagen Sie einfach (aktuelles Objekt) .attributes

.attributesgibt a hashvon any zurück object. Und es ist auch viel sauberer.

Austin Marusco
quelle
138
Beachten Sie, dass dies eine ActiveModel-spezifische Methode ist, keine Ruby-Methode.
Maurer
6
Im Fall von Sequel - verwenden Sie .values: sequel.jeremyevans.net/rdoc/classes/Sequel/Model/…
dimitarvp
instance_valueskann für alle Ruby-Objekte für die ähnliche Ausgabe verwendet werden.
Bishal
48

Implementieren #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
Levinalex
quelle
Technisch sollte es .to_hash sein, da # Klassenmethoden angibt.
Caleb
6
Nicht wirklich. In der RDoc-Dokumentation heißt es: Use :: for describing class methods, # for describing instance methods, and use . for example code(Quelle: ruby-doc.org/documentation-guidelines.html ) In der offiziellen Dokumentation (wie dem Ruby CHANGELOG, github.com/ruby/ruby/blob/v2_1_0/NEWS ) werden #beispielsweise Methoden und der Punkt verwendet für Klassenmethoden ziemlich konsequent.
Levinalex
Bitte verwenden Sie injizieren anstelle dieses Antimusters.
YoTengoUnLCD
each_with_objectinstance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
Einzeilige
43
Gift.new.instance_values # => {"name"=>"book", "price"=>15.95}
Erik Reedstrom
quelle
10
Dies ist Rails, Ruby selbst nicht instance_values. Beachten Sie, dass Matt nach einem Ruby-Weg gefragt hat, insbesondere nicht nach Rails.
Christopher Creutzig
28
Er sagte auch, zögern Sie nicht, auch die Rails-Antwort zu geben ... also tat ich es.
Erik Reedstrom
Gott, beide Versionen hier zu sehen;) Hat mir gefallen
Sebastian Schürmann
17

Sie können as_jsonMethode verwenden. Es wird Ihr Objekt in Hash konvertieren.

Dieser Hash wird jedoch als Wert für den Namen dieses Objekts als Schlüssel verwendet. In deinem Fall,

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

Wenn Sie einen Hash benötigen, der im Objekt gespeichert ist, verwenden Sie as_json(root: false). Ich denke, standardmäßig wird root falsch sein. Weitere Informationen finden Sie im offiziellen Ruby-Leitfaden

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

vicke4
quelle
13

Für aktive Datensatzobjekte

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

und dann einfach anrufen

gift.to_hash()
purch.to_hash() 
monika mevenkamp
quelle
2
lustig, es ist nicht Teil des Rails-Frameworks. Scheint eine nützliche Sache zu sein.
Magne
Die Attribute-Methode gibt einen neuen Hash mit den Werten in zurück, sodass in der to_hash-Methode kein weiterer erstellt werden muss. So: attribute_names.each_with_object ({}) {| name, attrs | attrs [name] = read_attribute (name)}. Siehe hier: github.com/rails/rails/blob/master/activerecord/lib/…
Chris Kimpton
Du hättest das mit Map machen können, deine Nebeneffekt-Implementierung tut meinem Verstand weh, Mann!
Nate Symer
11

Wenn Sie sich nicht in einer Rails-Umgebung befinden (dh ActiveRecord nicht verfügbar haben), kann dies hilfreich sein:

JSON.parse( object.to_json )
Andreas Rayo Kniep
quelle
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
tokland
quelle
6

Sie können eine sehr elegante Lösung mit einem funktionalen Stil schreiben.

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

Sie sollten die inspectMethode Ihres Objekts überschreiben , um den gewünschten Hash zurückzugeben, oder einfach eine ähnliche Methode implementieren, ohne das Standardobjektverhalten zu überschreiben.

Wenn Sie schicker werden möchten, können Sie die Instanzvariablen eines Objekts mit object.instance_variables durchlaufen

Dominic
quelle
4

Recursively Ihre Objekte Hash konvertieren mit 'hashable' gem ( https://rubygems.org/gems/hashable ) Beispiel

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}}]}
Mustafaturan
quelle
4

Vielleicht möchten Sie es versuchen instance_values. Das hat bei mir funktioniert.

Jäger
quelle
1

Erzeugt eine flache Kopie als Hash-Objekt nur der Modellattribute

my_hash_gift = gift.attributes.dup

Überprüfen Sie den Typ des resultierenden Objekts

my_hash_gift.class
=> Hash
Sean
quelle
0

Wenn Sie auch verschachtelte Objekte konvertieren möchten.

# @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 # }}}
Sergio Santiago
quelle
0

Gift.new.attributes.symbolize_keys

Lalit Kumar Maurya
quelle
0

Um dies ohne Rails zu tun, besteht eine saubere Möglichkeit darin, Attribute auf einer Konstanten zu speichern.

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

Um eine Instanz von Giftin eine zu konvertieren Hash, können Sie:

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

Dies ist ein guter Weg, da dies nur das enthält, was Sie definieren attr_accessor, und nicht jede Instanzvariable.

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}
Vinicius Brasilien
quelle
0

Ich habe angefangen, Strukturen zu verwenden, um Hash-Konvertierungen zu vereinfachen. Anstatt eine bloße Struktur zu verwenden, erstelle ich meine eigene Klasse, die aus einem Hash abgeleitet ist. Auf diese Weise können Sie Ihre eigenen Funktionen erstellen und die Eigenschaften einer Klasse dokumentieren.

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}
Alex
quelle