2

Since I'm dealing quite a lot with durations in my Rails app, I would also like to use them as hash keys in some places. However, it does not seem to work as expected for me.

Creating the initial hash works fine. For example, the following will work:

>> hash = {1.week => 'abc', 1.month => 'def'}

However, retrieving the values from the hash is not possible:

>> hash[1.month]
=> nil

Some further investigation showed me the reason for this:

>> 1.week.eql? 1.week
=> false

Which was quite unexpected

Furthermore, it seems like these objects behave differently to normal objects of FixNum class. For normal FixNum objects, the value of the object_id seems to be always the same, e.g:

>> 15.object_id
=> 31
>> 15.object_id
=> 31

For Durations, this is different, although they are from the same class

>> 1.month.class
=> Fixnum
>> 1.month.object_id
=> 70305513092140
>> 1.month.object_id
=> 70305513086860

So, it seems like the objects are always different, which is why hashes will not work. The only solution is to access with exactly the same object:

>> a = 1.month
=> 1 month
>> hash = {a => 'b'}
=> {1 month=>"b"}
>> hash[a]
=> "b"

Obviously, this is not always possible if you have a lot of objects with dynamically created durations.

Grouping by durations does not work either:

>> limits.group_by(&:duration)
=> #<OrderedHash {1 month=>[#<Limit:0x7fe28e441380>], 1 month=>[#<Limit:0x7fe28e4290c8>]}>

So, I'm wondering whether it is possible to get durations working as hash keys somehow? So far I have not found a good solution and I'm not sure if there is one. The important this is that functions like (Time.now - duration) should keep working.

FYI: My Ruby version - 1.8.7, Rails version - 2.3.18

dominos
  • 412
  • 1
  • 11
  • 21
  • 2
    Pass everything using `to_i` – oldergod Jul 29 '13 at 02:04
  • 1
    `1.month.class` is lying to you, it is actually an `ActiveSupport::Duration` even though it claims to be `Fixnum`. Looks like the lie has some holes in it. – mu is too short Jul 29 '13 at 02:26
  • @oldergod I considered that but using to_i is not actually the same always.For example Time.mktime(2013, 3, 6) - 1.month returns 6 Feb as an answer but Time.mktime(2013, 3, 6) - 1.month.to_i gives 4 Feb instead – dominos Jul 29 '13 at 07:33

1 Answers1

0

Wow, that is a weird finding. This does work, and I haven't been able to break it, but it seems a little fragile:

hash = {1.year.inspect => "val1", 2.month.inspect => "val2", (1.year-4.days).inspect => "val3"}
 => {"1 year"=>"val1", "2 months"=>"val2", "1 year and -4 days"=>"val3"}

hash[2.months.inspect]
 => "val2" 

hash[(1.year-4.days).inspect]
 => "val3" 

and to get the durations back

hash.keys.collect{|k| eval(k.gsub(" and ","+").split(" ").join("."))}
 => [1 year, 2 months, 1 year and -4 days] 
Erica Tripp
  • 316
  • 2
  • 8