Ruby Object'i Hash'e çevirir


127

Diyelim ki & Giftile bir nesnem var . Bunu Ruby'de Hash'e dönüştürmenin en iyi yolu nedir, Rails'e değil (Rails'in cevabını da vermekten çekinmeyin)?@name = "book"@price = 15.95{name: "book", price: 15.95}


16
@ Gift.attributes.to_options işe yarar mı?
Bay L

1
1) Hediye bir ActiveRecord nesnesi mi? 2) @ name / @ price'in yalnızca örnek değişkenleri olmadığını, aynı zamanda okuyucu erişimcileri olduğunu varsayabilir miyiz? 3) Bir hediyede sadece isim ve fiyat mı yoksa tüm nitelikleri mi istiyorsunuz?
tokland

@tokland, 1) bir, Giftbir @nash tanımlamıştır tam gibi , tabii, örnek değişkenler okuyucu erişimcileri sahip olabilir), 2 hariç. 3) Hediyedeki tüm özellikler.
ma11hew28

Tamam. Örnek değişkenleri / okuyucu erişimiyle ilgili soru, bir dış erişim (nash) veya iç yöntem (levinalex) isteyip istemediğini bilmekti. "İçeriden" yaklaşım için cevabımı güncelledim.
tokland

Yanıtlar:


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}

Alternatif olarak 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
Değişkeni başlatmayı atlamak için inject'i kullanabilirsiniz: gift.instance_variables.inject ({}) {| hash, var | karma [var.to_s.delete ("@")] = gift.instance_variable_get (var); hash}
Ürdün

8
Güzel. Sembolleri almak için var.to_s.delete("@")ile değiştirdim var[1..-1].to_sym.
ma11hew28

3
gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }; hash
Enjekte etme

1
Yakut fetişini asla anlamayacağım each. mapve injectçok daha güçlüdür. Bu Ruby ile yapmış bir tasarım qualm olduğunu mapve injectbirlikte uygulanmaktadır each. Bu sadece kötü bilgisayar bilimi.
Nate Symer

Biraz daha kısa:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin


48

Uygulamak #to_hashmı?

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

Teknik olarak, # sınıf yöntemlerini gösterdiğinden .to_hash olmalıdır.
Caleb

6
Aslında hayır. RDoc belgeleri şunu söylüyor: Use :: for describing class methods, # for describing instance methods, and use . for example code(kaynak: ruby-doc.org/documentation-guidelines.html ) Ayrıca, resmi belgeler (ruby CHANGELOG, github.com/ruby/ruby/blob/v2_1_0/NEWS gibi ) #örnek yöntemler ve nokta sınıf yöntemleri için oldukça tutarlı.
levinalex

Lütfen bu antipattern yerine enjekte kullanın.
YoTengoUnLCD

each_with_objectinstance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
Kullanan

43
Gift.new.instance_values # => {"name"=>"book", "price"=>15.95}

10
Bu Rails, Ruby'de yok instance_values. Matt'in bir Ruby yöntemi istediğini, özellikle Rails'i istemediğini unutmayın.
Christopher Creutzig

28
Rails cevabını da vermekten çekinmeyin dedi ... ben de yaptım.
Erik Reedstrom

Tanrı burada her iki sürümü görmek için;) bunu Beğendim
Sebastian Schürmann

17

Kullanabilirsiniz as_jsonYöntemi . Nesnenizi hash'e dönüştürecektir.

Ancak bu hash, anahtar olarak o nesnenin adına bir değer olarak gelecektir. Senin durumunda,

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

Nesne kullanımında depolanan bir hash'e ihtiyacınız varsa as_json(root: false). Varsayılan olarak kökün yanlış olacağını düşünüyorum. Daha fazla bilgi için resmi yakut kılavuzuna bakın

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


13

Aktif Kayıt Nesneleri için

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

ve sonra sadece ara

gift.to_hash()
purch.to_hash() 

2
komik, Rails çerçevesinin bir parçası değil. Orada olması yararlı bir şey gibi görünüyor.
Magne

Öznitelikler yöntemi, içindeki değerlerle yeni bir karma döndürür - bu nedenle to_hash yönteminde başka bir tane oluşturmaya gerek yoktur. Şöyle: öznitelik_names.each_with_object ({}) {| ad, attrs | attrs [ad] = okuma_ özniteliği (ad)}. Buraya bakın: github.com/rails/rails/blob/master/activerecord/lib/…
Chris Kimpton

Bunu harita ile yapabilirdin, yan etki uygulamanız aklımı acıtıyor dostum!
Nate Symer

11

Bir Rails ortamında değilseniz (yani ActiveRecord yoksa), bu yardımcı olabilir:

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

Fonksiyonel bir tarz kullanarak çok şık bir çözüm yazabilirsiniz.

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

4

Geçersiz kılmalısınız inspect istenen karma dönmek için nesnenin yöntemini, ya da sadece varsayılan nesne davranışını geçersiz kılma olmadan benzer bir yöntem uygulamak.

Daha meraklısı olmak istiyorsanız, object.instance_variables ile bir nesnenin örnek değişkenlerini yineleyebilirsiniz.


4

Yinelemeli kullanarak karma için nesneleri dönüştürmek 'hashable' taş ( https://rubygems.org/gems/hashable ) Örnek

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

Denemek isteyebilirsin instance_values. Bu benim için çalıştı.


1

Sadece model özniteliklerinin karma nesnesi olarak sığ bir kopya üretir

my_hash_gift = gift.attributes.dup

Ortaya çıkan nesnenin türünü kontrol edin

my_hash_gift.class
=> Hash

0

İç içe yerleştirilmiş nesnelerin de dönüştürülmesine ihtiyacınız varsa.

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

Bunu Rails olmadan yapmanın temiz bir yolu, nitelikleri bir sabit üzerinde depolamaktır.

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

Ve sonra, bir örneğini dönüştürmek için Giftbir etmek Hash, şunları yapabilirsiniz:

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

Bu, bunu yapmanın iyi bir yoludur çünkü attr_accessorher örnek değişkeni değil, yalnızca tanımladığınız şeyi içerecektir .

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

Dönüşümleri kolay hale getirmek için yapıları kullanmaya başladım. Çıplak bir yapı kullanmak yerine, bir hash'den türetilen kendi sınıfımı yaratırım bu, kendi işlevlerinizi oluşturmanıza olanak tanır ve bir sınıfın özelliklerini belgeler.

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}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.