I am trying to make the fastest #to_struct
method in Ruby's Hash.
I am including a use case and benchmark so you can run and see if you have really improved the code.
This is my implementation and the benchmark is included. The time at the bottom is the time it takes on my machine. How can I make this faster?
require "json"
require 'benchmark'
require 'bigdecimal/math'
class Hash
def to_struct
k = self.keys
klass = k.map(&:to_s).sort_by {|word| word.downcase}.join.capitalize
begin
Kernel.const_get("Struct::" + klass).new(*self.values_at(*k))
rescue NameError
Struct.new(klass, *(k)).new(*self.values_at(*k))
end
end
end
# You have a hash that you have built in your app
sample_hash = {
foo_key: "foo_val",
bar_key: "bar_val",
baz_key: "baz_val",
foo1_key: "foo_val",
bar1_key: "bar_val",
baz1_key: "baz_val",
foo2_key: "foo_val",
bar2_key: "bar_val",
baz2_key: "baz_val",
foo3_key: "foo_val",
bar3_key: "bar_val",
baz3_key: "baz_val",
foo4_key: "foo_val",
bar4_key: "bar_val",
baz4_key: "baz_val",
foo5_key: "foo_val",
bar5_key: "bar_val",
baz5_key: "baz_val",
foo6_key: "foo_val",
bar6_key: "bar_val",
baz6_key: "baz_val",
foo7_key: "foo_val",
bar7_key: "bar_val",
baz7_key: "baz_val",
}
# Then you have JSON coming from some external api
json_response = "{\"qux_key\":\"qux_val\",\"quux_key\":\"quux_val\",\"corge_key\":\"corge_val\"}"
hash_with_unknown_keys = JSON.parse(json_response)
# Merge these two together
sample_hash.merge!(hash_with_unknown_keys)
iterations = 100_000
Benchmark.bm do |bm|
bm.report "#to_struct" do
iterations.times do
# Would be super nice if I could convert this to a struct with a method
# Somehow a bit faster than the explicit example below and much faster than open struct
sample_struct = sample_hash.to_struct
unless sample_struct.foo_key == "foo_val"
raise "Wrong value"
end
end
end
bm.report "Struct" do
iterations.times do
sample_struct = Struct.new(*sample_hash.keys)
.new(*sample_hash.values)
unless sample_struct.foo_key == "foo_val"
raise "Wrong value"
end
end
end
bm.report "OpenStruct" do
iterations.times do
sample_open_struct = OpenStruct.new(sample_hash)
unless sample_open_struct.foo_key == "foo_val"
raise "Wrong value"
end
end
end
end
# user system total real # #to_struct 4.030000 0.010000 4.040000 ( 4.072031) # Struct 6.870000 0.290000 7.160000 ( 7.320459) # OpenStruct 23.550000 0.210000 23.760000 ( 23.895187)