Convert Hash to Openstruct Recursively

Convert Hash to OpenStruct recursively

personally I use the recursive-open-struct gem - it's then as simple as RecursiveOpenStruct.new(<nested_hash>)

But for the sake of recursion practice, I'll show you a fresh solution:

require 'ostruct'

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

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

edit

Weihang Jian showed a slight improvement to this here https://stackoverflow.com/a/69311716/2981429

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

Also see https://stackoverflow.com/a/63264908/2981429 which shows how to handle arrays

note

the reason this is better than the JSON-based solutions is because you can lose some data when you convert to JSON. For example if you convert a Time object to JSON and then parse it, it will be a string. There are many other examples of this:

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

yeah ... not super useful. You've completely lost your reference to the actual instance.

How to convert recursive/nested OpenStruct Object to Hash

Check out docs.

You can use OpenStruct#marshal_dump:

openstruct_object.marshal_dump

OpenStruct#to_h will work, too:

openstruct_object.to_h

You can convert your object to hash and then hash to JSON:

openstruct_object.to_h.to_json

But it looks like what you want is a Hash object, not JSON object.

Ruby: Convert nested hash to object?

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

Ruby - Hash to methods recursively

You could use OpenStruct to make a data structure like that. For example:

require 'ostruct'
s = OpenStruct.new(a: 1, b: 2, c: OpenStruct.new(a: 1, b: 2, c: 3))
s.c.a # => 1
s.c # => #<OpenStruct a=1, b=2, c=3>

Convert a hash into a struct

If it doesn't specifically have to be a Struct and instead can be an OpenStruct:

pry(main)> require 'ostruct'
pry(main)> s = OpenStruct.new(h)
=> #<OpenStruct a=1, b=2>
pry(main)> puts s.a, s.b
1
2

Ruby: Convert nested hash to object?

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

Deep Convert OpenStruct to JSON

There is no default methods to accomplish such task because the built-in #to_hash returns the Hash representation but it doesn't deep converts the values.

If a value is an OpenStruct, it's returned as such and it's not converted into an Hash.

However, this is not that complicated to solve. You can create a method that traverses each key/value in an OpenStruct instance (e.g. using each_pair), recursively descends into the nested OpenStructs if the value is an OpenStruct and returns an Hash of just Ruby basic types.

Such Hash can then easily be serialized using either .to_json or JSON.dump(hash).

This is a very quick example, with an update from @Yuval Rimar for arrays of OpenStructs:

def openstruct_to_hash(object, hash = {})
case object
when OpenStruct then
object.each_pair do |key, value|
hash[key] = openstruct_to_hash(value)
end
hash
when Array then
object.map { |v| openstruct_to_hash(v) }
else object
end
end

openstruct_to_hash(OpenStruct.new(foo: 1, bar: OpenStruct.new(baz: 2)))
# => {:foo=>1, :bar=>{:baz=>2}}

How do I convert hash keys to method names?

You could just wrap up your hash in an OpenStruct:

require 'ostruct'
tempData = {"a" => 100, "here" => 200, "c" => "hello"}
os = OpenStruct.new tempData
os.a #=> 100
os.here #=> 200

If you really really wanted to, you could also monkey-patch the Hash class, but I'd advise against that:

class Hash
def method_missing(m, *args, &blk)
fetch(m) { fetch(m.to_s) { super } }
end
end

tempData = {"a" => 100, "here" => 200, "c" => "hello"}
tempData.a #=> 100

Update: In my personal extensions library I added a Hash#to_ostruct method. This will recursively convert a hash into an OpenStruct including all nested hashes.

Flattening nested hash to a single hash with Ruby/Rails

You could do this:

def flatten_hash(hash)
hash.each_with_object({}) do |(k, v), h|
if v.is_a? Hash
flatten_hash(v).map do |h_k, h_v|
h["#{k}.#{h_k}".to_sym] = h_v
end
else
h[k] = v
end
end
end

flatten_hash(:foo => "bar",
:hello => {
:world => "Hello World",
:bro => "What's up dude?",
},
:a => {
:b => {
:c => "d"
}
})
# => {:foo=>"bar",
# => :"hello.world"=>"Hello World",
# => :"hello.bro"=>"What's up dude?",
# => :"a.b.c"=>"d"}


Related Topics



Leave a reply



Submit