2014-11-07 17 views
13

Tôi đang cố chuyển băm và băm lồng vào đối tượng.chuyển đổi băm thành đối tượng

cho đến nay đối tượng băm đầu tiên được chuyển đổi thành công bởi mã này:

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 

Nhưng vấn đề là, tôi cũng muốn chuyển đổi đối tượng băm lồng nhau. nhưng không thể làm được.

h = Hashit.new({a: '123r', b: {c: 'sdvs'}}) 
=> #<Hashit:0x00000006516c78 @a="123r", @b={:c=>"sdvs"}> 

xem @b={:c=>"sdvs"} phần này ở đầu ra. Tôi cũng muốn chuyển đổi nó thành đối tượng. liệu có thể nếu có thì làm thế nào?

+0

Nếu bạn hỏi cho 'h' có biến dụ' [: @a,: @b,: @c] ', như @Ben và tôi cho rằng, câu trả lời bạn đã chọn không chính xác. –

Trả lời

3

Bạn cần phải thêm đệ quy:

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">> 
+0

+1, tôi vừa hiểu nhầm nó. – mohameddiaa27

+1

@ mohameddiaa27 câu trả lời của bạn cũng hữu ích nhưng openstruct là điều tốt nhưng khác nhau. – hehehuhu

+0

'h.instance_variables => [: @a,: @b]'. Chúng ta không muốn '=> [: @a,: @b,: @c]'? –

1

Bạn có thể kiểm tra các loại trên v khi bạn khởi tạo đối tượng và gọi new để có được một mới Hashit khi nó là một hash khác.

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 

và đoạn phát sinh từ trước sẽ là:

h = Hashit.new({a: '123r', b: {c: 'sdvs'}}) 
=> #<Hashit:0x007fa71421a850 @a="123r", @b=#<Hashit:0x007fa71421a5a8 @c="sdvs">> 
+0

Đẹp, Ben, mặc dù tôi nghĩ rằng cách OP của việc xác định đọc và ghi riêng biệt là không cần thiết phức tạp, và không đọc cũng như chỉ 'self.class.send (: attr_accessor, k)'. –

0

Nếu tôi hiểu câu hỏi một cách chính xác, điều này sẽ làm điều đó:

class Hashit 
    def initialize(hash) 
    convert_to_obj(hash) 
    end 

    private 

    def convert_to_obj(h) 
    h.each do |k,v| 
     self.class.send(:attr_accessor, k) 
     instance_variable_set("@#{k}", v) 
     convert_to_obj(v) if v.is_a? Hash 
    end 
    end 
end 

h = Hashit.new({ a: '123r', 
     b: { c: 'sdvs', d: { e: { f: 'cat' }, g: {h: 'dog'} } } }) 
    #=> #<Hashit:0x000001018eee58 @a="123r", 
    #  @b={:c=>"sdvs", :d=>{:e=>{:f=>"cat"}, :g=>{:h=>"dog"}}}, 
    #  @c="sdvs", @d={:e=>{:f=>"cat"}, :g=>{:h=>"dog"}}, 
    #  @e={:f=>"cat"}, @f="cat", @g={:h=>"dog"}, @h="dog"> 
h.instance_variables 
    #=> [:@a, :@b, :@c, :@d, :@e, :@f, :@g, :@h] 
Hashit.instance_methods(false) 
    #=> [:a, :a=, :b, :b=, :c, :c=, :d, :d=, :e, :e=, :f, :f=, :g, :g=, :h, :h=] 
h.d 
    #=> {:e=>{:f=>"cat"}} 
h.d = "cat" 
h.d 
    #=> "cat" 
18

Một cách khác là sử dụng JSON và OpenStruct , là tiêu chuẩn của 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 
+2

Ồ, tôi đã bỏ lỡ điều này cho _years_! –

+1

Đây là cách tốt nhất. –

+0

OpenStruct.new ({... không quá sâu, như ví dụ hoàn hảo này. –

Các vấn đề liên quan