2014-11-07 19 views
13

Sto provando a convertire hash e hash nidificati in oggetti.convertire hash nell'oggetto

finora oggetto primo hash viene convertito con successo da questo codice:

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 

Ma il problema è, voglio anche per convertire gli oggetti hash nidificati. ma non ce l'ho fatta

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

vedere @b={:c=>"sdvs"} questa parte in uscita. Voglio anche convertirlo in oggetto. è possibile se sì allora come?

+0

Se si chiede 'h' di avere variabili di istanza' [: @a,: @b,: @c] ', come @ Ben e ho assunto, la risposta che selezionato non è corretto. –

risposta

3

è necessario aggiungere ricorsività:

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, l'ho appena frainteso. – mohameddiaa27

+1

@ mohameddiaa27 anche la tua risposta è stata utile, ma openstruct è buono ma diverso. – hehehuhu

+0

'h.instance_variables => [: @a,: @b]'. Non vogliamo '=> [: @a,: @b,: @c]'? –

1

Si potrebbe verificare il tipo sul v quando si inizializza l'oggetto e chiamare new per ottenere una nuova Hashit quando si tratta di un altro hash.

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 

e frammento risultante da prima sarebbe:

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

Bello, Ben, anche se penso che il modo OP di definire accessori separati in lettura e scrittura sia inutilmente complesso e non sia leggibile come "self.class.send (: attr_accessor, k)". –

0

Se ho capito bene la domanda, questo dovrebbe farlo:

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

Un altro modo è quello di utilizzare JSON e OpenStruct , che sono lib di ruby ​​standard:

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

Oh, mi manca questo per _years_! –

+1

Questo è il migliore. –

+0

OpenStruct.new ({... non così profondo, come questo esempio perfetto –