How can I convert a hash into a struct in ruby?
Given this:
h = { :a => 1, :b => 2 }
I want a struct such that:
s.a == 1
s.b == 2
How can I convert a hash into a struct in ruby?
Given this:
h = { :a => 1, :b => 2 }
I want a struct such that:
s.a == 1
s.b == 2
This is based on @elado's answer above, but using the keyword_init value (Struct Documentation)
You could simply do this:
Person = Struct.new(:first_name, :last_name, :age, keyword_init: true)
person_hash = { first_name: "Foo", last_name: "Bar", age: 29 }
person = Person.new(person_hash)
=> #<struct Person first_name="Foo", last_name="Bar", age=29>
This gives a clean plain read-only object, similar to a ruby Struct but with deep conversion and extra to_h method to get struct at any point as Hash.
Example
foo = {a:{b:{c:123}}}.to_struct
foo.a.b.c # 123
foo.a.to_h # {b:{c:123}}
Ruby code
class Hash
def to_struct
Class.new.tap do |c|
c.define_singleton_method(:to_h) do
m_list = methods(false) - [:to_h]
m_list.inject({}) do |h, m|
h[m] = send(m)
h[m] = h[m].to_h if h[m].class == Class
h
end
end
each do |k, v|
v = v.to_struct if v.class == Hash
c.define_singleton_method(k) { v }
end
end
end
end
Not exactly the answer to a question (not a ruby Struct object), but I needed just this while looking for an answer, so I will just post the answer here.
You can convert from Hash to Struct using the following code:
Struct.new(*my_hash.keys.map(&:to_sym)).new(*my_hash.values)
Ensure you convert all keys to symbols, as it will error on String keys, NameError: identifier my_key needs to be constant
I personally recommend adding a monkey patch to the Hash class because this is such a powerful action
# config/initializers/core_extensions.rb
Hash.class_eval do
def to_struct
Struct.new(*keys.map(&:to_sym)).new(*values)
end
end
If you need a recursive version, here's a neat hack/solution
a_hash = {a: {b: {c: 'x'}}}
structs_inside_structs = JSON.parse(
a_hash.to_json, object_class: OpenStruct
)
# => #<OpenStruct a=#<OpenStruct b=#<OpenStruct c="x">>>
structs_inside_structs.a.b.c
# => "x"
TL;DR;
use OpenStruct.new(hash)
New Better way
hash = {"Name" => "Taimoor", "id" => "222", "SomeKey" => "Some value", "attributes" => {"type" => 'User', 'role' => 'manager'}}
OpenStruct.new(hash)
This will only convert 1st level of hash to struct. To convert nested attributes hash to struct I did this
hash.attributes = OpenStruct.new(hash.attributes)
OLD Way
I had a hash with string keys
{"Name" => "Taimoor", "id" => "222", "SomeKey" => "Some value"}
So I need to first convert keys to symbols hash.keys.map(&:to_sym) and to access those keys in the original hash, I used the hash.with_indifferent_access method on the hash.
def hash_to_struct(hash)
Struct.new(*(k = hash.keys.map(&:to_sym)))
.new(*hash.with_indifferent_access.values_at(*k))
end
Now it will work for both symbol and string type keys of the hash.
Note: This will convert the hash to struct at one level only. For nested hash, you need to call this method on each level of nesting.