convert hash to object
Solution 1
You need to add recursivity:
class Hashit
def initialize(hash)
hash.each do |k,v|
self.instance_variable_set("@#{k}", v.is_a?(Hash) ? Hashit.new(v) : v)
self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
end
end
end
h = Hashit.new({a: '123r', b: {c: 'sdvs'}})
# => #<Hashit:0x007fa6029f4f70 @a="123r", @b=#<Hashit:0x007fa6029f4d18 @c="sdvs">>
Solution 2
You can use OpenStruct http://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html
user = OpenStruct.new({name: "Jimmy Cool", age: "25"})
user.name #Jimmy Cool
user.age #25
Solution 3
Another way is to use JSON and OpenStruct, which are standard ruby libs:
irb:
> require 'JSON'
=> true
> r = JSON.parse({a: { b: { c: 1 }}}.to_json, object_class: OpenStruct)
=> #<OpenStruct a=#<OpenStruct b=#<OpenStruct c=1>>>
> r.a.b.c
=> 1
Solution 4
Ruby has an inbuilt data structure OpenStruct to solve something like this. Still, there is a problem. It is not recursive. So, you can extend OpenStruct class like this:
# Keep this in lib/open_struct.rb
class OpenStruct
def initialize(hash = nil)
@table = {}
if hash
hash.each_pair do |k, v|
k = k.to_sym
@table[k] = v.is_a?(Hash) ? OpenStruct.new(v) : v
end
end
end
def method_missing(mid, *args) # :nodoc:
len = args.length
if mname = mid[/.*(?==\z)/m]
if len != 1
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
end
modifiable?[new_ostruct_member!(mname)] = args[0].is_a?(Hash) ? OpenStruct.new(args[0]) : args[0]
elsif len == 0 # and /\A[a-z_]\w*\z/ =~ mid #
if @table.key?(mid)
new_ostruct_member!(mid) unless frozen?
@table[mid]
end
else
begin
super
rescue NoMethodError => err
err.backtrace.shift
raise
end
end
end
end
and remember to require 'open_struct.rb'
next time you want to use OpenStruct.
Now you can do something like this:
person = OpenStruct.new
person.name = "John Smith"
person.age = 70
person.more_info = {interests: ['singing', 'dancing'], tech_skills: ['Ruby', 'C++']}
puts person.more_info.interests
puts person.more_info.tech_skills
Related videos on Youtube
hehehuhu
Updated on October 15, 2020Comments
-
hehehuhu over 3 years
I am trying to convert hash and nested hashes to objects.
so far first hash object is converted successfully by this code:
class Hashit def initialize(hash) hash.each do |k,v| self.instance_variable_set("@#{k}", v) self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")}) self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)}) end end end
But problem is, i also want to convert nested hash objects. but couldn't make it.
h = Hashit.new({a: '123r', b: {c: 'sdvs'}}) => #<Hashit:0x00000006516c78 @a="123r", @b={:c=>"sdvs"}>
see
@b={:c=>"sdvs"}
this part in output. I want also to convert it to object. is it possible if yes then how?-
Cary Swoveland over 9 yearsIf you asking for
h
to have instance variables[:@a, :@b, :@c]
, as @Ben and I assumed, the answer you selected is incorrect.
-
-
hehehuhu over 9 years@mohameddiaa27 your answer was also helpful but openstruct is good but different thing.
-
Cary Swoveland over 9 years
h.instance_variables => [:@a, :@b]
. Do we not want=> [:@a, :@b, :@c]
? -
Cary Swoveland over 9 yearsNice one, Ben, though I think the OP's way of defining separate read and write accessors is unnecessarily complex, and does not read as well as just
self.class.send(:attr_accessor, k)
. -
kaibakker almost 8 yearsThis is the best answer!
-
Sergio Belevskij about 7 yearsOpenStruct.new({... not so deep, as this perfect example.
-
Patrick Dougall over 6 yearsWill also coerce your values to string or int :(
-
Allen over 6 yearsThis way is not recursive. You can't
object.a.b.c
, you can onlyobject.a
. -
Mark Schneider about 2 yearsDoes not work for keys to which Hash class responds. For instance, try:
r = JSON.parse({a: { b: { display: 1 }}}.to_json, object_class: OpenStruct)