Forum: Ruby Assistance dynamically building a nested hash

Posted by Matthew Williams (mdweezer)
on 2008-01-28 20:06
I've done some searching and have found some nested hash references but
not quite enough to get me going with what I need.

I'm looking to build a structure like so...

actuals = {actual_id, {date => count, date => count, date => count}}

An example would be like:

actuals = {"1", {"01-01-2008" => 5, "01-02-2008" => 10}}

Then I can later iterate through the hash and for each ID, get any date
associated with it an associated count.

I'm pulling the data in with an ActiveRecord find that will return a
dozen or so records for a given id but with various dates and counts.

So records returned could look like

1, "01-02-2008", 5
1, "01-03-2008", 10
2, "12-25-2007", 5
1, "01-04-2008", 15

Should result in a hash when I'm finished like so:

actuals =
{"1",{"01-02-2008" => 5, "01-03-2008" => 10 "01-04-2008" => 15}},
{"2",{"12-25-2007" => 5}}

Iterating through those records is no problem...  I just can't wrap my
brain around the nested hash of dates and counts for a given id.

I'm assuming this is the appropriate and most effective data structure
for this data but I would be up for any other suggestions.

I appreciate any responses, thanks.
Posted by 7stud -- (7stud)
on 2008-01-28 20:51
Matthew Williams wrote:
> I've done some searching and have found some nested hash references but
> not quite enough to get me going with what I need.
> 
> I'm looking to build a structure like so...
> 
> actuals = {actual_id, {date => count, date => count, date => count}}
> 
> An example would be like:
> 
> actuals = {"1", {"01-01-2008" => 5, "01-02-2008" => 10}}
> 
> Then I can later iterate through the hash and for each ID, get any date
> associated with it an associated count.
> 
> I'm pulling the data in with an ActiveRecord find that will return a
> dozen or so records for a given id but with various dates and counts.
> 
> So records returned could look like
> 
> 1, "01-02-2008", 5
> 1, "01-03-2008", 10
> 2, "12-25-2007", 5
> 1, "01-04-2008", 15
> 
> Should result in a hash when I'm finished like so:
> 
> actuals =
> {"1",{"01-02-2008" => 5, "01-03-2008" => 10 "01-04-2008" => 15}},
> {"2",{"12-25-2007" => 5}}
> 
> Iterating through those records is no problem...  I just can't wrap my
> brain around the nested hash of dates and counts for a given id.
> 
> I'm assuming this is the appropriate and most effective data structure
> for this data but I would be up for any other suggestions.
> 
> I appreciate any responses, thanks.

arr = [
[1, "01-02-2008", 5],
[1, "01-03-2008", 10],
[2, "12-25-2007", 5],
[1, "01-04-2008", 15]
]


#Create a hash where looking up a non-existent key
#in the hash results in the key being created and
#assigned an empty hash:
results = Hash.new do |hash, key|
  hash[key] = {}
end

arr.each do |sub_arr|
  main_key = sub_arr[0]
  sub_key = sub_arr[1]

  #Create new key, value in sub hash:
  results[main_key][sub_key] = sub_arr[2]
end

p results

--output:--
{1=>{"01-04-2008"=>15, "01-03-2008"=>10, "01-02-2008"=>5}, 
2=>{"12-25-2007"=>5}}
Posted by Gary Wright (Guest)
on 2008-01-28 21:08
(Received via mailing list)
On Jan 28, 2008, at 2:51 PM, 7stud -- wrote:
> #Create a hash where looking up a non-existent key
> #in the hash results in the key being created and
> #assigned an empty hash:
> results = Hash.new do |hash, key|
>   hash[key] = {}
> end

This works for a two-level hash, which is what the OP needed.

If you want an infinite-level hash:

lazy = lambda { |h,k| h[k] = Hash.new(&lazy) }

h = Hash.new(&lazy)

h[1][2][3][4] = 5

p h     # {1=>{2=>{3=>{4=>5}}}}


Gary Wright
Posted by Jens Wille (Guest)
on 2008-01-28 21:44
(Received via mailing list)
Gary Wright [2008-01-28 21:07]:
> This works for a two-level hash, which is what the OP needed.
> 
> If you want an infinite-level hash:
> 
> lazy = lambda { |h,k| h[k] = Hash.new(&lazy) }
> 
> h = Hash.new(&lazy)
you can even do it like this:

  h = Hash.new { |l, k| l[k] = Hash.new(&l.default_proc) }

;-)

> h[1][2][3][4] = 5
> 
> p h     # {1=>{2=>{3=>{4=>5}}}}

cheers
jens
Posted by Matthew Williams (mdweezer)
on 2008-01-28 21:55
I'm trying to work with this code as we speak, I'm still a bit of a Ruby 
novice so I'm still getting used to the idioms involved...

7stud -- wrote:
> 
> arr = [
> [1, "01-02-2008", 5],
> [1, "01-03-2008", 10],
> [2, "12-25-2007", 5],
> [1, "01-04-2008", 15]
> ]
> 

I'm running .to_a on my ActiveRecord find result set which is properly 
making it an array.

> 
> #Create a hash where looking up a non-existent key
> #in the hash results in the key being created and
> #assigned an empty hash:
> results = Hash.new do |hash, key|
>   hash[key] = {}
> end
> 
> arr.each do |sub_arr|
>   main_key = sub_arr[0]
>   sub_key = sub_arr[1]
> 
>   #Create new key, value in sub hash:
>   results[main_key][sub_key] = sub_arr[2]
> end
> 

Once all of those methods run against my result set I don't seem to end 
up with the result you ended up with.  I'm running the debugger against 
it and I'm trying to follow the path to nail down why it isn't exactly 
working....

My code:

@actuals = Actual.find(:all, :select => 'obs_number, week_ending, 
labor_hours', :conditions => ["week_ending >= ? AND project_number = ? 
AND primary_number = ?", @week_ending, @charge.project_number, 
@charge.primary_number]).to_a

    results = Hash.new do |hash, key|
      hash[key] = {}
    end

    @actuals.each do |sub_arr|
      main_key = sub_arr[0]
      sub_key = sub_arr[1]
      #Create new key, value in sub hash:
      results[main_key][sub_key] = sub_arr[2]
    end

After that runs I'm left with results as a single hash with no data. 
I'll continue with the debugging and hopefully end up with a successful 
result soon enough.  Thanks for the responses!

> p results
> 
> --output:--
> {1=>{"01-04-2008"=>15, "01-03-2008"=>10, "01-02-2008"=>5}, 
> 2=>{"12-25-2007"=>5}}
Posted by Gary Wright (Guest)
on 2008-01-28 22:09
(Received via mailing list)
On Jan 28, 2008, at 3:43 PM, Jens Wille wrote:
>   h = Hash.new { |l, k| l[k] = Hash.new(&l.default_proc) }

slick
Posted by Matthew Williams (mdweezer)
on 2008-01-28 22:26
> 
> My code:
> 
> @actuals = Actual.find(:all, :select => 'obs_number, week_ending, 
> labor_hours', :conditions => ["week_ending >= ? AND project_number = ? 
> AND primary_number = ?", @week_ending, @charge.project_number, 
> @charge.primary_number]).to_a
> 
>     results = Hash.new do |hash, key|
>       hash[key] = {}
>     end
> 
>     @actuals.each do |sub_arr|
>       main_key = sub_arr[0]
>       sub_key = sub_arr[1]
>       #Create new key, value in sub hash:
>       results[main_key][sub_key] = sub_arr[2]
>     end
> 
> After that runs I'm left with results as a single hash with no data. 
> I'll continue with the debugging and hopefully end up with a successful 
> result soon enough.  Thanks for the responses!
> 

sub_arr was being treated as an actual object data type... So a few 
quick changes to sub_arr to reference the attributes instead of indexes 
and I'm all set.

I'm still working on my iterator to appropriately sort through them in 
my view but I appreciate the help a lot on getting the ball rolling...

Take care.
Posted by Aarthi Ruby (learn_ruby)
on 2010-03-10 02:34
Hi,

Can you please post the solutions if you have resolved this? I am kind 
of stuck at the same state as yours :-(
Appreciate any help.

Thanks much!

>> 
>> My code:
>> 
>> @actuals = Actual.find(:all, :select => 'obs_number, week_ending, 
>> labor_hours', :conditions => ["week_ending >= ? AND project_number = ? 
>> AND primary_number = ?", @week_ending, @charge.project_number, 
>> @charge.primary_number]).to_a
>> 
>>     results = Hash.new do |hash, key|
>>       hash[key] = {}
>>     end
>> 
>>     @actuals.each do |sub_arr|
>>       main_key = sub_arr[0]
>>       sub_key = sub_arr[1]
>>       #Create new key, value in sub hash:
>>       results[main_key][sub_key] = sub_arr[2]
>>     end
>> 
>> After that runs I'm left with results as a single hash with no data. 
>> I'll continue with the debugging and hopefully end up with a successful 
>> result soon enough.  Thanks for the responses!
>> 
> 
> sub_arr was being treated as an actual object data type... So a few 
> quick changes to sub_arr to reference the attributes instead of indexes 
> and I'm all set.
> 
> I'm still working on my iterator to appropriately sort through them in 
> my view but I appreciate the help a lot on getting the ball rolling...
> 
> Take care.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.