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 OpenStruct
s 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
Safe Navigation Equivalent to Rails Try for Hashes
How to Get the File Extension from a Url
How to Create a Form in Rails Without Having to Use Form_For and a Model Instance
Why Doesn't Module.Method_Defined(:Method) Work Correctly
Including a Virtual Attribute in the Respond_With Hash
Capistrano 3 + Sprockets 3 + Rails 4.2.1 Won't Deploy
Setting Environment Variables with Puppet
Trouble Downgrading Ruby on Os X Mavericks
How to Allow Binary File Download Using Grape API
Has Anyone Figured Out a Way to Run the Same Cucumber Scenario on Multiple Browsers/Web Drivers
Omniauth Facebook Expired Token Error
Passing Binding or Arguments to Erb from the Command Line
Architecture for a Modular, Component-Based Sinatra Application
How to Do Named Capture in Ruby