Ruby - efficiently storing data in deeply nested Hash?
I'm writing a script the stores db info in a hash to be later available for easy access. My code looks something like this:
my_hash = {}
connection.query("select * from ...").each_hash do |row|
date = row['date']
ip = row['ip']
port = row['port']
resp = row['avg_resp_time'].to_i
unless my_hash.key?(ip)
my_hash[ip] = { date => { port => resp } }
else
unless my_hash[ip].key?(date)
my_hash[ip][date] = { port => resp }
else
unless my_hash[ip][date].key?(port)
my_hash[ip][date][port] = resp
end
end
end
end
This is just a sample, but the actual hash is nested sometimes 5-6 levels deep, and takes a long time to build for a large result set. I realize there's a tradeo开发者_StackOverflow中文版ff here between time-to-build-map / time-to-access-data, but I was wondering if there's a more efficient way to store data to be just as easily accessed in code, or a way to make my current script a bit more efficient - this hash building loop is my bottleneck at the moment. thanks.
The idiomatic Ruby for this is to use the Hash contructor for missing keys:
>> data = Hash.new { |hash, key| hash[key] = {} }
>> data[:a][:b] = {:x => 5}
>> data
=> {:a=>{:b=>{:x=>5}}}
[edit] I am not sure you need it here, but the above code works only for one nested level. For infinite nesting:
class Hash
def self.new_recursive
self.new { |hash, key| hash[key] = self.new_recursive }
end
end
data = Hash.new_recursive
....
However, I tend to dislike non-functional solutions, so you may look Enumerable#group_by, or some kind of grouping in the SQL level, of Enumerable#inject to build the nested hashes. Just some raw ideas so you can play a bit.
What about assigning an array as a key? For example
my_hash[[ip, date, port]] = resp
or even
my_hash[[ip, date, port, resp]] = true
depending on your purpose. This will reduce unnecessary nesting, and increase efficiency.
精彩评论