Ruby convert Object to Hash

151,363

Solution 1

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}

Alternatively with 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}

Solution 2

Just say (current object) .attributes

.attributes returns a hash of any object. And it's much cleaner too.

Solution 3

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

Solution 4

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

Solution 5

You can use as_json method. It'll convert your object into hash.

But, that hash will come as a value to the name of that object as a key. In your case,

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

If you need a hash that's stored in the object use as_json(root: false). I think by default root will be false. For more info refer official ruby guide

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

Share:
151,363
ma11hew28
Author by

ma11hew28

Updated on December 11, 2021

Comments

  • ma11hew28
    ma11hew28 over 2 years

    Let's say I have a Gift object with @name = "book" & @price = 15.95. What's the best way to convert that to the Hash {name: "book", price: 15.95} in Ruby, not Rails (although feel free to give the Rails answer too)?

  • Jordan
    Jordan about 13 years
    You can use inject to skip initializing the variable: gift.instance_variables.inject({}) { |hash,var| hash[var.to_s.delete("@")] = gift.instance_variable_get(var); hash }
  • ma11hew28
    ma11hew28 about 13 years
    Nice. I replaced var.to_s.delete("@") with var[1..-1].to_sym to get symbols.
  • Christopher Creutzig
    Christopher Creutzig about 12 years
    This is Rails, Ruby itself doesn't have instance_values. Note that Matt asked for a Ruby way, specifically not Rails.
  • Erik Reedstrom
    Erik Reedstrom about 12 years
    He also said feel free to give the Rails answer as well... so I did.
  • bricker
    bricker over 11 years
    Note that this is an ActiveModel-specific method, not a Ruby method.
  • Magne
    Magne over 11 years
    funny it's not part of the Rails framework. Seems like a useful thing to have there.
  • Griehle
    Griehle about 11 years
    The attributes method returns a new hash with the values in - so no need to create another in the to_hash method. Like so: attribute_names.each_with_object({}) { |name, attrs| attrs[name] = read_attribute(name) } . See here: github.com/rails/rails/blob/master/activerecord/lib/…
  • Narfanator
    Narfanator almost 11 years
    Don't use inject, use gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) } and get rid of the trailing ; hash
  • dimitarvp
    dimitarvp about 10 years
    In the case of Sequel -- use .values: sequel.jeremyevans.net/rdoc/classes/Sequel/Model/…
  • Caleb
    Caleb about 9 years
    Technically, it should be .to_hash, since # indicates class methods.
  • levinalex
    levinalex about 9 years
    Actually, no. RDoc documentation says: Use :: for describing class methods, # for describing instance methods, and use . for example code (source: ruby-doc.org/documentation-guidelines.html) Also, official documentation (like the ruby CHANGELOG, github.com/ruby/ruby/blob/v2_1_0/NEWS) uses # for instance methods and the dot for class methods pretty consistently.
  • Nate Symer
    Nate Symer about 9 years
    I will never understand the ruby fetish for each. map and inject are much more powerful. This is one design qualm I have with Ruby: map and inject are implemented with each. It's simply bad computer science.
  • Nate Symer
    Nate Symer about 8 years
    you could have done this with map, your side-effect implementation is hurting my mind man!
  • YoTengoUnLCD
    YoTengoUnLCD over 7 years
    Please use inject instead of this antipattern.
  • anothermh
    anothermh about 7 years
    One-liner variant using each_with_object: instance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
  • Sebastian Schürmann
    Sebastian Schürmann about 7 years
    God to see both versions here ;) Liked it
  • bishal
    bishal over 5 years
    instance_values can be used for all ruby objects for the similar output.
  • Marvin
    Marvin about 5 years
    Slightly more concise: hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
  • Aurelio
    Aurelio about 3 years
    Not a ruby expert, but isn't this a much cleaner method than all others? It looks like the best answer to me, or am I missing something?
  • Teoman shipahi
    Teoman shipahi over 2 years
    Very helpful, thanks you!