Reopening Classes

Can someone offer an explaination as to what I’m encountering here…


This code doesn’t work…

class Time
def <=>(other_time)
self.strftime("%H%M%S%Y%m%d").to_i <=>
other_time.strftime("%H%M%S%Y%m%d").to_i
end
end

first = Time.local(2006, 10, 21)
last = Time.local(2006, 10, 27)

time_slots = []
range = first…last
range.step(1.hour) do |day_slot|
time_slots << day_slot
end

time_slots.sort

the time_slots array isn’t populating properly

This does

first = Time.local(2006, 10, 21)
last = Time.local(2006, 10, 27)

time_slots = []
range = first…last
range.step(1.hour) do |day_slot|
time_slots << day_slot
end

class Time
def <=>(other_time)
self.strftime("%H%M%S%Y%m%d").to_i <=>
other_time.strftime("%H%M%S%Y%m%d").to_i
end
end

time_slots.sort

Tony

On 25/10/06, [email protected] [email protected] wrote:

Can someone offer an explaination as to what I’m encountering here…

Range#each employs <=>, so by changing the semantics of the
comparison, you break your step loop. When you redefine <=> after the
loop, it doesn’t affect it, so your second example works.

Instead of altering Time, you could just do this:

time_slots.sort_by{ |ts| ts.strftime(“%H%M%S%Y%m%d”).to_i }

Paul.

On Wed, 25 Oct 2006, [email protected] wrote:

end
time_slots.sort

end
end

time_slots.sort

i see you already got an answer, but you might want to know about this
idiom:

harp:~ > cat a.rb
lt = lambda{|*a| Time.local *a}
hr = 3600
times = []
fields = %w( hour min sec year month day )

( lt[2006, 10, 21] … lt[2006, 10, 23] ).step(hr){|t| times << t}

sorted = times.sort_by{|t| fields.map{|f| t.send f}}

puts sorted.first(16)

harp:~ > ruby a.rb
Sat Oct 21 00:00:00 MDT 2006
Sun Oct 22 00:00:00 MDT 2006
Mon Oct 23 00:00:00 MDT 2006
Sat Oct 21 01:00:00 MDT 2006
Sun Oct 22 01:00:00 MDT 2006
Sat Oct 21 02:00:00 MDT 2006
Sun Oct 22 02:00:00 MDT 2006
Sat Oct 21 03:00:00 MDT 2006
Sun Oct 22 03:00:00 MDT 2006
Sat Oct 21 04:00:00 MDT 2006
Sun Oct 22 04:00:00 MDT 2006
Sat Oct 21 05:00:00 MDT 2006
Sun Oct 22 05:00:00 MDT 2006
Sat Oct 21 06:00:00 MDT 2006
Sun Oct 22 06:00:00 MDT 2006
Sat Oct 21 07:00:00 MDT 2006

the basic idea of

fields = %w( one two three )

list.sort_by{|elem| fields.map{|f| elem.send f}}

is powerful.

regards.

-a

Another way to compute timeslots which avoids this might be:

timeslots = (0…6*24).map {|h| first + h.hour}

Generalizing this to arbitray start and end times is left as an
exercise to the reader.

Something like this ?

add a to date method for Time class

class Time
def to_date
Date.new(year, month, day)
rescue NameError
nil
end
end

first date and last date

first = Time.local(2006, 10, 21, 0, 0, 0)
last = Time.local(2006, 10, 28, 0, 0, 0)

calculate the number of days between them

range = last.to_date - first.to_date

create your array

timeslots = (0..range*24).map {|h| first + h.hour}

remove the last one as it’ll be the first time slot of the next day

@timeslots = timeslots[0, timeslots.size - 1]

sort by timeslot then by day

@timeslots.sort! {|a, b| a.strftime("%H%M%S%Y%m%d").to_i <=>

b.strftime("%H%M%S%Y%m%d").to_i}

Tony

On 10/25/06, Paul B. [email protected] wrote:

time_slots.sort_by{ |ts| ts.strftime(“%H%M%S%Y%m%d”).to_i }
Another observation.

Building a range of times and then using step to obtain each hour is
pretty inefficient.

Range#step basically works like this I might have an off by one bug
but it gets the idea across:

class Range
def step(incr = 1)
elt = self.begin
end = self.end
ctr = 0
while ((elt <=> end)
if (ctr == 0)
yield elt
ctr = incr
end
elt = elt.succ
ctr -= 1
end
end

And Time#succ gives the time one second later than the receiver.

So the while look is going to be executed once for every second in the
range, in this case 6 days * 24 hours * 60 minutes * 60 seconds, so to
get
the 144 Time objects actually needed, a total of 518400 will be
allocated.

This costs time and memory, and GC cycles, which may or may not be
important.

Another way to compute timeslots which avoids this might be:

timeslots = (0…6*24).map {|h| first + h.hour}

Generalizing this to arbitray start and end times is left as an
exercise to the reader.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Another way to compute timeslots which avoids this might be:

timeslots = (0…6*24).map {|h| first + h.hour}

Generalizing this to arbitray start and end times is left as an
exercise to the reader.

Maybe this ? so you represent day segments

start_time = Time.gm(2006, 10, 31, 0, 0, 0)
slots = 3
no_of_days = 3

schedule =[]
timeslots = (0…slots).map{|h| start_time + h.hour}
timeslots.each do |timeslot|
(0…no_of_days).map do |i|
schedule << timeslot+i.day
end
end
puts schedule

Tony

On Wed, 25 Oct 2006 05:40:51 -0700, "anthony.green wrote:

end
time_slots.sort

end
end

time_slots.sort

You can use a custom comparison function by passing a block to sort, so
there’s no reason to override the spaceship operator (that is, the <=>
operator).

You could use time_slots.sort {|x,y| x.strftime("%H%M%S%Y%m%d").to_i
<=> y.strftime("%H%M%S%Y%m%d").to_i}

It’s probably more efficient to use sort_by, as that only computes each
strftime once:

time_slots.sort_by{|x| x.strftime("%H%M%S%Y%m%d").to_i }

–Ken

On Fri, 27 Oct 2006, [email protected] wrote:

slots = 3

Tony

http://runt.rubyforge.org/

-a

On 10/26/06, [email protected] [email protected] wrote:

Maybe this ? so you represent day segments
end
end
puts schedule

Tony

http://runt.rubyforge.org/

I remember very well when someone bashed me because I reported a broken
link, I am still wondering why, anyway
Ara is the link ok, I can go to rubyforge but not to runt, or was that
“rant” :wink:
R.

-a


my religion is very simple. my religion is kindness. – the dalai lama


The reasonable man adapts himself to the world; the unreasonable one
persists in trying to adapt the world to himself. Therefore all progress
depends on the unreasonable man.

  • George Bernard Shaw