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)?


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}

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


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

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}

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


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() 

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 )

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

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

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


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}}]}

4

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


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

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


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}

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}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.