Подтвердить что ты не робот

Преобразование Hash в OpenStruct рекурсивно

Учитывая, что у меня есть этот хеш:

 h = { a: 'a', b: 'b', c: { d: 'd', e: 'e'} }

И я конвертирую в OpenStruct:

o = OpenStruct.new(h)
 => #<OpenStruct a="a", b="b", c={:d=>"d", :e=>"e"}> 
o.a
 => "a" 
o.b
 => "b" 
o.c
 => {:d=>"d", :e=>"e"} 
2.1.2 :006 > o.c.d
NoMethodError: undefined method `d' for {:d=>"d", :e=>"e"}:Hash

Я хочу, чтобы все вложенные ключи были методами. Поэтому я могу получить доступ к d как таковой:

o.c.d
=> "d"

Как я могу это достичь?

4b9b3361

Ответ 1

лично я использую гем recursive-open-struct - он так же прост, как RecursiveOpenStruct.new(<nested_hash>)

Но для практики рекурсии я покажу вам свежее решение:

require 'ostruct'

def to_recursive_ostruct(hash)
  OpenStruct.new(hash.each_with_object({}) do |(key, val), memo|
    memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
  end)
end

puts to_recursive_ostruct(a: { b: 1}).a.b
# => 1

редактировать

причина, по которой это лучше, чем решения на основе JSON, заключается в том, что вы можете потерять некоторые данные при конвертации в JSON. Например, если вы преобразуете объект Time в JSON, а затем проанализируете его, это будет строка. Есть много других примеров этого:

class Foo; end
JSON.parse({obj: Foo.new}.to_json)["obj"]
# => "#<Foo:0x00007fc8720198b0>"

да... не супер полезно. Вы полностью потеряли свою ссылку на фактический экземпляр.

Ответ 3

Я придумал это решение:

h = { a: 'a', b: 'b', c: { d: 'd', e: 'e'} }
json = h.to_json
=> "{\"a\":\"a\",\"b\":\"b\",\"c\":{\"d\":\"d\",\"e\":\"e\"}}" 
object = JSON.parse(json, object_class:OpenStruct)
object.c.d
 => "d" 

Чтобы это сработало, мне пришлось сделать дополнительный шаг: конвертировать его в json.

Ответ 4

Вот рекурсивное решение, которое избегает преобразования хэша в json:

def to_o(obj)
  if obj.is_a?(Hash)
    return OpenStruct.new(obj.map{ |key, val| [ key, to_o(val) ] }.to_h)
  elsif obj.is_a?(Array)
    return obj.map{ |o| to_o(o) }
  else # Assumed to be a primitive value
    return obj
  end
end