Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby weirdness: x == y && [x, y].uniq == [x, y]

I have an two-element array of ActiveRecord objects, slots_to_import. These objects have begin_at columns, and thus attributes. I was trying to get objects that have unique begin_at values. Alas, slots_to_import.uniq_by(&:begin_at) did not work. But the begin_at values are equal for the two objects:

(rdb:1) p slots_to_import.first.begin_at == slots_to_import.last.begin_at
true
(rdb:1) p slots_to_import.uniq_by(&:begin_at).map(&:begin_at)
[Mon, 26 Nov 2012 19:00:00 UTC +00:00, Mon, 26 Nov 2012 19:00:00 UTC +00:00]
(rdb:1) p [slots_to_import.first.begin_at, slots_to_import.last.begin_at].uniq
[Mon, 26 Nov 2012 19:00:00 UTC +00:00, Mon, 26 Nov 2012 19:00:00 UTC +00:00]

Some more checking around:

(rdb:1) p [slots_to_import.first.begin_at.to_datetime, slots_to_import.last.begin_at.to_datetime].uniq
[Mon, 26 Nov 2012 19:00:00 +0000]
(rdb:1) p [slots_to_import.first.begin_at.usec, slots_to_import.last.begin_at.usec].uniq
[0]
(rdb:1) p [slots_to_import.first.begin_at.to_f, slots_to_import.last.begin_at.to_f].uniq
[1353956400.0]
(rdb:1) p [slots_to_import.first.begin_at.utc, slots_to_import.last.begin_at.utc].uniq
[Mon, 26 Nov 2012 19:00:00 +0000]
(rdb:1) p [slots_to_import.first.begin_at, slots_to_import.last.begin_at].uniq
[Mon, 26 Nov 2012 19:00:00 UTC +00:00, Mon, 26 Nov 2012 19:00:00 UTC +00:00]

I thought perhaps that uniq was checking whether they were the same object (since they were not). But no, some noodling in my rails console showed me that it does not use an object id check:

1.8.7 :111 > x = Time.zone.parse("Mon, 29 Oct 2012 19:29:17 UTC +00:00")
 => Mon, 29 Oct 2012 19:29:17 UTC +00:00 
1.8.7 :112 > y = Time.zone.parse("Mon, 29 Oct 2012 19:29:17 UTC +00:00")
 => Mon, 29 Oct 2012 19:29:17 UTC +00:00 
1.8.7 :113 > x == y
 => true 
1.8.7 :114 > [x, y].uniq
 => [Mon, 29 Oct 2012 19:29:17 UTC +00:00] 

I'm using Ruby 1.8.7p358 and ActiveSupport 3.2.0. BTW, I can solve my own issue by simply adding a to_datetime, but I'm really curious why this isn't working without a conversion.

like image 969
ehsanul Avatar asked Oct 29 '12 20:10

ehsanul


1 Answers

I thought you were working with plain Time objects, so I started by looking at time.c from the Ruby platform source (1.9.3). If they were Ruby 1.9.3 Time objects, you could try:

[x.to_r, y.to_r]

Now I know you're working with ActiveSupport::TimeWithZone objects. (As a suggestion, it would be good to mention key information like this next time you post a question.) Here is the body of ActiveSupport::TimeWithZone#eql?:

def eql?(other)
  utc == other
end

And here is the hash function:

alias :hash, :to_i

So the next step is for you to show what you get from [x.utc, y.utc, x.to_i, y.to_i, x.utc.class, y.utc.class].

like image 107
Alex D Avatar answered Oct 23 '22 04:10

Alex D