[QUIZ] Time Window (#144)

Here's my solution. It converts a time window into an array of arrays of ranges. Each range matches an interval of time (e.g.: 500..1700); each subarray contains all the ranges for a given day. I then wrote the simple Array#some method that returns true if the given predicate returns true for at least one of its elements, which I used to write TimeWindow#incude?.

$days = {"Sun"=>0,
              "Mon"=>1,
              "Tue"=>2,
              "Wed"=>3,
              "Thu"=>4,
              "Fri"=>5,
              "Sat"=>6}

class Array
  def some
    each {|el| return true if yield el}
    false
  end
end

class TimeWindow
  def initialize(win_str)
    @times = ([nil]*7).map{[]}
    win_str << " " #In case of empty
    win_str.split(/;/).each do |win|
      days_str = win.match(/(((#{$days.keys.join('|')}|)( |-)?)*)/)[0]..strip
      days = []
      days_str.scan(/#{$days.keys.join('|')}/) do |day|
        days << $days[day]
      end
      days_str.scan(/(#{$days.keys.join('|')})-(#{$days.keys.join('|')})/) do
        a = $days[$1]
        b = $days[$2]
        days += (a..(b > a ? b : b+7))..to_a.map{|d|d%7}
      end
      days = (0..6).to_a if days.empty?
      
      times = []
      win.scan(/(\d{4})-(\d{4})/) do
        times << (($1.to_i)...($2.to_i))
      end
      times = [0..2400] if times.empty?
      
      days.each do |d|
        times.each do |t|
          @times[d] << t
        end
      end
    end
    
    def include?(time)
      @times[time.wday].some{|trange| trange === (time.hour*100+time.min)}
    end
  end
end

···

----- Original Message ----
From: Ruby Quiz <james@grayproductions.net>
To: ruby-talk ML <ruby-talk@ruby-lang.org>
Sent: Friday, October 19, 2007 7:14:00 AM
Subject: [QUIZ] Time Window (#144)

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this
quiz until
48 hours have passed from the time on this message.

2.. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby Talk follow the discussion. Please reply to the original quiz
message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Brian Candler

Write a Ruby class which can tell you whether the current time (or any
given
time) is within a particular "time window". Time windows are defined by
strings
in the following format:

    # 0700-0900 # every day between these times
    # Sat Sun # all day Sat and Sun, no other
times
    # Sat Sun 0700-0900 # 0700-0900 on Sat and Sun only
    # Mon-Fri 0700-0900 # 0700-0900 on Monday to Friday
only
    # Mon-Fri 0700-0900; Sat Sun # ditto plus all day Sat and Sun
    # Fri-Mon 0700-0900 # 0700-0900 on Fri Sat Sun Mon
    # Sat 0700-0800; Sun 0800-0900 # 0700-0800 on Sat, plus
0800-0900 on Sun

Time ranges should exclude the upper bound, i.e. 0700-0900 is 07:00:00
to
08:59:59. An empty time window means "all times everyday". Here are
some test
cases to make it clearer:

    class TestTimeWindow < Test::Unit::TestCase
      def test_window_1
        w = TimeWindow.new("Sat-Sun; Mon Wed 0700-0900; Thu 0700-0900
1000-1200")
        
        assert ! w.include?(Time.mktime(2007,9,25,8,0,0)) # Tue
        assert w.include?(Time.mktime(2007,9,26,8,0,0)) # Wed
        assert ! w.include?(Time.mktime(2007,9,26,11,0,0))
        assert ! w.include?(Time.mktime(2007,9,27,6,59,59)) # Thu
        assert w.include?(Time.mktime(2007,9,27,7,0,0))
        assert w.include?(Time.mktime(2007,9,27,8,59,59))
        assert ! w.include?(Time.mktime(2007,9,27,9,0,0))
        assert w.include?(Time.mktime(2007,9,27,11,0,0))
        assert w.include?(Time.mktime(2007,9,29,11,0,0)) # Sat
        assert w.include?(Time.mktime(2007,9,29,0,0,0))
        assert w.include?(Time.mktime(2007,9,29,23,59,59))
      end
      
      def test_window_2
        w = TimeWindow.new("Fri-Mon")
        assert ! w.include?(Time.mktime(2007,9,27)) # Thu
        assert w.include?(Time.mktime(2007,9,28))
        assert w.include?(Time.mktime(2007,9,29))
        assert w.include?(Time.mktime(2007,9,30))
        assert w.include?(Time.mktime(2007,10,1))
        assert ! w.include?(Time.mktime(2007,10,2)) # Tue
      end
      
      def test_window_nil
        w = RDS::TimeWindow.new("")
        assert w.include?(Time.mktime(2007,9,25,1,2,3)) # all times
      end
    end

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

Isn't this the same as Enumerable#any?

Jesus.

···

On 10/24/07, James Koppel <jamesbkoppel@yahoo.com> wrote:

I then wrote the simple Array#some method that returns true if the given
predicate returns true for at least one of its elements, which I used to write
TimeWindow#incude?.

class Array
  def some
    each {|el| return true if yield el}
    false
  end
end

Here's my solution. It converts a time window into an array of arrays of
ranges. Each range matches an interval of time (e.g.: 500..1700); each
subarray contains all the ranges for a given day. I then wrote the
simple Array#some method that returns true if the given predicate
returns true for at least one of its elements, which I used to write
TimeWindow#incude?.

$days = {"Sun"=>0,
              "Mon"=>1,
              "Tue"=>2,
              "Wed"=>3,
              "Thu"=>4,
              "Fri"=>5,
              "Sat"=>6}

I suggest using a constant rather than a global variable for this. You
can put the constant in the TimeWindow class.

class Array
  def some
    each {|el| return true if yield el}
    false
  end
end

This has already been discussed as being equal to Enumerable#any?

class TimeWindow
  def initialize(win_str)
    @times = ([nil]*7).map{}

This is the same as Array.new(7){}, which is a more usual way to
express this.

    win_str << " " #In case of empty
    win_str.split(/;/).each do |win|
      days_str = win.match(/(((#{$days.keys.join('|')}|)(
      >-)?)*)/)[0].strip days =
      days_str.scan(/#{$days.keys.join('|')}/) do |day|
        days << $days[day]
      end
      days_str.scan(/(#{$days.keys.join('|')})-(#{$days.keys.join

('|')})/)

      do
        a = $days[$1]
        b = $days[$2]
        days += (a..(b > a ? b : b+7)).to_a.map{|d|d%7}
      end
      days = (0..6).to_a if days.empty?
      
      times =
      win.scan(/(\d{4})-(\d{4})/) do
        times << (($1.to_i)...($2.to_i))
      end
      times = [0..2400] if times.empty?
      
      days.each do |d|
        times.each do |t|
          @times[d] << t
        end
      end
    end
    
    def include?(time)
      @times[time.wday].some{|trange| trange ===
      (time.hour*100+time.min)}
    end
  end
end

Looks generally like my solution.

--Ken

···

On Wed, 24 Oct 2007 10:04:56 +0900, James Koppel wrote:

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

That's the same thought that I had. Assuming that any? didn't exist, I am still fearful of adding methods to core classes. I think core classes should only be modified in special cases, like building a framework.

How do the rest of us feel about this?

- Philip

···

On Oct 24, 2007, at 12:16 AM, Jesús Gabriel y Galán wrote:

On 10/24/07, James Koppel <jamesbkoppel@yahoo.com> wrote:

I then wrote the simple Array#some method that returns true if the given
predicate returns true for at least one of its elements, which I used to write
TimeWindow#incude?.

class Array
  def some
    each {|el| return true if yield el}
    false
  end
end

Isn't this the same as Enumerable#any?

Jesus.

It sure is.

James Edward Gray II

···

On Oct 24, 2007, at 2:16 AM, Jesús Gabriel y Galán wrote:

On 10/24/07, James Koppel <jamesbkoppel@yahoo.com> wrote:

I then wrote the simple Array#some method that returns true if the given
predicate returns true for at least one of its elements, which I used to write
TimeWindow#incude?.

class Array
  def some
    each {|el| return true if yield el}
    false
  end
end

Isn't this the same as Enumerable#any?

This is also just an Array disguised as a Hash.

James Edward Gray II

···

On Oct 24, 2007, at 9:10 AM, Ken Bloom wrote:

On Wed, 24 Oct 2007 10:04:56 +0900, James Koppel wrote:

Here's my solution. It converts a time window into an array of arrays of
ranges. Each range matches an interval of time (e.g.: 500..1700); each
subarray contains all the ranges for a given day. I then wrote the
simple Array#some method that returns true if the given predicate
returns true for at least one of its elements, which I used to write
TimeWindow#incude?.

$days = {"Sun"=>0,
              "Mon"=>1,
              "Tue"=>2,
              "Wed"=>3,
              "Thu"=>4,
              "Fri"=>5,
              "Sat"=>6}

I suggest using a constant rather than a global variable for this. You
can put the constant in the TimeWindow class.

Well, first of all it depends if you are building a library that
someone will use, or just a program that will run independently. If
it's the former, even then I would think that just adding methods is
OK, as long as the method makes sense. What should be done with really
great care (or not at all) is modifying existing methods. But I'm
fairly new to Ruby so take my opinion with a grain of salt.

Jesus.

···

On 10/24/07, Philip Gatt <gattster@gmail.com> wrote:

That's the same thought that I had. Assuming that any? didn't exist,
I am still fearful of adding methods to core classes. I think core
classes should only be modified in special cases, like building a
framework.

How do the rest of us feel about this?

Like most things, it's just another tool in your belt. You'll need to consider when to use it and when not to.

On the plus side, slowly transforming Ruby into the language of a problem is a powerful way to work. The minus is that you can make it hard to use your code with other Ruby solutions in the process.

You will need to decide when that's a good trade-off and when it's not. Ruby trusts you to make the choice.

James Edward Gray II

···

On Oct 24, 2007, at 3:04 AM, Philip Gatt wrote:

That's the same thought that I had. Assuming that any? didn't exist, I am still fearful of adding methods to core classes. I think core classes should only be modified in special cases, like building a framework.

How do the rest of us feel about this?

It could be a lot worse. Which of us hasn't run across something like
this?

days = {
  0 => 'Sun',
  1 => 'Mon',
  2 => 'Tue',
  3 => 'Wed',
  4 => 'Thu',
  5 => 'Fri',
  6 => 'Sat',
}

···

On Oct 24, 9:16 am, James Edward Gray II <ja...@grayproductions.net> wrote:

On Oct 24, 2007, at 9:10 AM, Ken Bloom wrote:

> On Wed, 24 Oct 2007 10:04:56 +0900, James Koppel wrote:

>> Here's my solution. It converts atimewindowinto an array of
>> arrays of
>> ranges. Each range matches an interval oftime(e.g.: 500..1700);
>> each
>> subarray contains all the ranges for a given day. I then wrote the
>> simple Array#some method that returns true if the given predicate
>> returns true for at least one of its elements, which I used to write
>> TimeWindow#incude?.

>> $days = {"Sun"=>0,
>> "Mon"=>1,
>> "Tue"=>2,
>> "Wed"=>3,
>> "Thu"=>4,
>> "Fri"=>5,
>> "Sat"=>6}

> I suggest using a constant rather than a global variable for this. You
> can put the constant in the TimeWindow class.

This is also just an Array disguised as a Hash.

James Edward Gray II

--
-yossef