Ruby mengonversi objek ke Hash

127

Katakanlah saya punya Giftobjek dengan @name = "book"& @price = 15.95. Apa cara terbaik untuk mengonversinya menjadi Hash {name: "book", price: 15.95}di Ruby, bukan Rails (walaupun merasa bebas untuk memberikan jawaban Rails juga)?

ma11hew28
sumber
16
Apakah @ gift.attributes.to_options akan berlaku?
Tn. L
1
1) Apakah hadiah adalah objek ActiveRecord? 2) dapatkah kita menganggap @ name / @ harga bukan hanya variabel instan tetapi juga pembaca pembaca? 3) Anda hanya ingin nama dan harga atau semua atribut dalam hadiah apa pun itu?
tokland
@tokland, 1) tidak ada, Giftadalah persis seperti @nash telah ditetapkan , kecuali, 2) yakin, variabel instance dapat memiliki accesor pembaca. 3) Semua atribut dalam hadiah.
ma11hew28
Baik. Pertanyaan tentang variabel instan / akses pembaca adalah untuk mengetahui apakah menginginkan akses luar (nash) atau metode di dalam (levinalex). Saya memperbarui jawaban saya untuk pendekatan "dalam".
tokland

Jawaban:

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}

Atau dengan 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
sumber
3
Anda dapat menggunakan inject untuk melewati menginisialisasi variabel: gift.instance_variables.inject ({}) {| hash, var | hash [var.to_s.delete ("@")] = gift.instance_variable_get (var); hash}
Jordan
8
Bagus. Saya diganti var.to_s.delete("@")dengan var[1..-1].to_symuntuk mendapatkan simbol.
ma11hew28
3
Jangan gunakan suntikan, gunakan, gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }dan singkirkan jejaknya; hash
Narfanator
1
Aku tidak akan pernah mengerti apa itu jimat ruby each. mapdan injectjauh lebih kuat. Ini adalah salah satu keraguan desain yang saya miliki dengan Ruby: mapdan injectditerapkan dengan each. Ini hanya ilmu komputer yang buruk.
Nate Symer
Sedikit lebih ringkas:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin
293

Katakan saja (objek saat ini) .attributes

.attributesmengembalikan hashapapun object. Dan juga jauh lebih bersih.

Austin Marusco
sumber
138
Perhatikan bahwa ini adalah metode khusus ActiveModel, bukan metode Ruby.
bricker
6
Dalam kasus Sequel - gunakan .values: sequel.jeremyevans.net/rdoc/classes/Sequel/Model/…
dimitarvp
instance_valuesdapat digunakan untuk semua objek ruby ​​untuk output yang sama.
bishal
48

Menerapkan #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
sumber
Secara teknis, seharusnya .to_hash, karena # mengindikasikan metode kelas.
Caleb
6
Sebenarnya tidak. Dokumentasi RDoc mengatakan: Use :: for describing class methods, # for describing instance methods, and use . for example code(sumber: ruby-doc.org/documentation-guidelines.html ) Juga, dokumentasi resmi (seperti CHANGELOG ruby, github.com/ruby/ruby/roby/blob/v2_1_0/NEWS ) menggunakan #metode instan dan titik untuk metode kelas cukup konsisten.
levinalex
Silakan gunakan suntikan bukan antipattern ini.
YoTengoUnLCD
Satu varian menggunakan 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}
Erik Reedstrom
sumber
10
Ini Rails, Ruby sendiri tidak punya instance_values. Perhatikan bahwa Matt meminta cara Ruby, khususnya bukan Rails.
Christopher Creutzig
28
Dia juga mengatakan merasa bebas untuk memberikan jawaban Rails juga ... jadi saya lakukan.
Erik Reedstrom
Tuhan melihat kedua versi di sini;) Menyukainya
Sebastian Schürmann
17

Anda bisa menggunakan as_jsonmetode. Ini akan mengubah objek Anda menjadi hash.

Tapi, hash itu akan datang sebagai nilai untuk nama objek itu sebagai kunci. Dalam kasus anda,

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

Jika Anda membutuhkan hash yang disimpan dalam penggunaan objek as_json(root: false). Saya pikir secara default root akan salah. Untuk info lebih lanjut lihat panduan ruby ​​resmi

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

vicke4
sumber
13

Untuk Objek Rekaman Aktif

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

lalu panggil saja

gift.to_hash()
purch.to_hash() 
monika mevenkamp
sumber
2
lucu itu bukan bagian dari kerangka Rails. Sepertinya hal yang berguna untuk ada di sana.
Magne
Metode atribut mengembalikan hash baru dengan nilai-nilai di - jadi tidak perlu membuat yang lain di metode to_hash. Seperti itu: attribute_names.each_with_object ({}) {| name, attrs | attrs [name] = read_attribute (name)}. Lihat di sini: github.com/rails/rails/blob/master/activerecord/lib/…
Chris Kimpton
Anda bisa melakukan ini dengan peta, implementasi efek samping Anda melukai pikiran saya!
Nate Symer
11

Jika Anda tidak berada dalam lingkungan Rails (mis. Tidak memiliki ActiveRecord tersedia), ini mungkin membantu:

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

Anda dapat menulis solusi yang sangat elegan menggunakan gaya fungsional.

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

Anda harus mengganti inspectmetode objek Anda untuk mengembalikan hash yang diinginkan, atau hanya menerapkan metode yang sama tanpa mengabaikan perilaku objek default.

Jika Anda ingin mendapatkan pelamun, Anda dapat beralih di atas variabel instance objek dengan object.instance_variables

Dominic
sumber
4

Konversi objek Anda secara hash menjadi hash menggunakan permata 'hashable' ( https://rubygems.org/gems/hashable ) Contoh

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
sumber
4

Mungkin ingin mencoba instance_values. Itu berhasil bagi saya.

Pemburu
sumber
1

Menghasilkan salinan dangkal sebagai objek hash hanya atribut model

my_hash_gift = gift.attributes.dup

Periksa jenis objek yang dihasilkan

my_hash_gift.class
=> Hash
Sean
sumber
0

Jika Anda perlu objek bersarang untuk dikonversi juga.

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

Gift.new.attributes.symbolize_keys

Lalit Kumar Maurya
sumber
0

Untuk melakukan ini tanpa Rails, cara yang bersih adalah dengan menyimpan atribut pada konstanta.

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

Dan kemudian, untuk mengonversi instance Giftke Hash, Anda dapat:

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

Ini adalah cara yang baik untuk melakukan ini karena hanya akan memasukkan apa yang Anda definisikan attr_accessor, dan tidak setiap variabel instan.

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 Brasil
sumber
0

Saya mulai menggunakan struct untuk mempermudah konversi hash. Alih-alih menggunakan bare struct saya membuat kelas saya sendiri yang berasal dari hash ini memungkinkan Anda untuk membuat fungsi Anda sendiri dan mendokumentasikan properti kelas.

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
sumber