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


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}

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


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

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}

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


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

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 )

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

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

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


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

4

Mungkin ingin mencoba instance_values. Itu berhasil bagi saya.


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

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


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}

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}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.