Collect only N weekdays including current date, but no weekends

To solve the problem as I mentioned in the subject, I wrote the below :

date = Date.today
dates = [date]

loop do
  break if dates.size == 8 # Here N is 8
  date += 1
  dates << date if (1..5).include?(date.wday)
End

dates

# => [#<Date: 2015-02-26 ((2457080j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-02-27 ((2457081j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-03-02 ((2457084j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-03-03 ((2457085j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-03-04 ((2457086j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-03-05 ((2457087j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-03-06 ((2457088j,0s,0n),+0s,2299161j)>,
# #<Date: 2015-03-09 ((2457091j,0s,0n),+0s,2299161j)>]

What else you guys have in mind ? :slight_smile:

···

--

Regards,
Arup Rakshit

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

--Brian Kernighan

What else you guys have in mind ? :slight_smile:

Write some tests. Cover your edge cases. Make them pass.

date = Date.today
dates = [date]

What if the script is run on saturday?

···

On Feb 25, 2015, at 11:19, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

>
> What else you guys have in mind ? :slight_smile:

Write some tests. Cover your edge cases. Make them pass.

Excellent suggestions.

> date = Date.today
> dates = [date]

What if the script is run on saturday?

Good pointer, I missed it.

Here is my modified code :

# date_helper.rb

require 'date'

def only_weekdays(start_date, n)
  dates =

  loop do
    break if dates.size == n
    dates << start_date if (1..5).include?(start_date.wday)
    start_date += 1
  end

  dates
end

Spec for the method.

require_relative "../a.rb"

describe "#only_weekdays" do
  context "when start_date is Sunday" do
    let(:start_date) { Date.parse "22/02/2015" }
    let(:week_days) { only_weekdays(start_date, 4) }

    it "doesn't include Sunday" do
      expect(week_days).not_to include(start_date)
    end

    it "returns 4 weekdays" do
      expect(week_days.size).to eq(4)
    end
  end

  context "when start_date is Saturday" do
    let(:start_date) { Date.parse "21/02/2015" }
    let(:weekends) { ["21/02/2015", "22/02/2015"].map(&Date.method(:parse)) }
    let(:week_days) { only_weekdays(start_date, 4) }

    it "excludes Saturday and Sunday" do
      expect(week_days).not_to include(*weekends)
    end

    it "returns 4 weekdays" do
      expect(week_days.size).to eq(4)
    end
  end

  context "when start_date is Thrusday" do
    let(:start_date) { Date.parse "19/02/2015" }
    let(:weekends) { ["21/02/2015", "22/02/2015"].map(&Date.method(:parse)) }
    let(:week_days) { only_weekdays(start_date, 4) }

    it "excludes weekends" do
      expect(week_days).not_to include(*weekends)
    end

    it "returns 4 weekdays" do
      expect(week_days.size).to eq(4)
    end
  end
end

Result :

···

On Wednesday, February 25, 2015 12:38:36 PM Ryan Davis wrote:

> On Feb 25, 2015, at 11:19, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

====

[arup@Ruby]$ rspec spec/a_spec.rb
......

Finished in 0.01237 seconds (files took 0.84944 seconds to load)
6 examples, 0 failures

[arup@Ruby]$

I'll be looking for some other solutions to the problem. :slight_smile:

Again thanks to Ryan. :smiley:

--

Regards,
Arup Rakshit

Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.

--Brian Kernighan

I'll be looking for some other solutions to the problem. :slight_smile:

If I understood your requirements correctly, I think this will do it.
If not, maybe it will give you some ideas.

require 'date'
def weekdays(d,n)
   (0..((n+4)/5)*7).map{|x| d+x}.select{|y| (1..5).include?(y.wday)}[0...n]
end

p weekdays(Date.today,8)

Harry

···

--
Online Karnaugh map solver with circuit

you can try collect then select, eg,

$cat weekdays.rb

require 'date'

class Date
  def weekend?
    saturday? || sunday?
  end
end

def weekdays startdate=Date.today, ffdays = 7
  (startdate..(startdate+ffdays)).select{|d| !d.weekend? }
end

if $0 == __FILE__
  w=weekdays.map{|d| d.to_s+" "+Date::DAYNAMES[d.wday]}
  puts "#{(p w).size} weekdays"
end

$ruby weekdays.rb
["2015-03-09 Monday", "2015-03-10 Tuesday", "2015-03-11 Wednesday",
"2015-03-12 Thursday", "2015-03-13 Friday", "2015-03-16 Monday"]
6 weekdays

···

On Thu, Feb 26, 2015 at 11:46 AM, Arup Rakshit <aruprakshit@rocketmail.com> wrote:

I'll be looking for some other solutions to the problem. :slight_smile:

I'll be looking for some other solutions to the problem. :slight_smile:

A slightly shorter version :slight_smile:

require 'date'

def weekdays(d,n)
   (0..(n+4)/5*7).map{|x| d+x}.select{|y| 55%(y.wday+5)>0}[0...n]
end

p weekdays(Date.today,8)

Harry

···

--
Online Karnaugh map solver with circuit

https://gist.github.com/shvetsovdm/b3a9797bdf67ff3c7296

a more verbose solution

require 'date'

def next_n_work_days n

  today = Date.today

  number_of_weekends_days = ((n / 5) *2)

  (1..(number_of_weekends_days + n) - 1).reduce([today]) do |days,
plus_num_days|

    day = today + plus_num_days

    # or

    # day = days.last.next

    # and you don't have to use plus_num_days var

    days << day if !day.saturday? and !day.sunday?

    days

  end

end

···

2015-02-26 19:23 GMT+10:00 Harry <rubystuff@32x8.com>:

I'll be looking for some other solutions to the problem. :slight_smile:

If I understood your requirements correctly, I think this will do it.
If not, maybe it will give you some ideas.

require 'date'
def weekdays(d,n)
  (0..((n+4)/5)*7).map{|x| d+x}.select{|y| (1..5).include?(y.wday)}[0...n]
end

p weekdays(Date.today,8)

Harry

--
Online Karnaugh map solver with circuit

http://www.32x8.com

def weekdays startdate=Date.today, ffdays = 7
  (startdate..(startdate+ffdays)).select{|d| !d.weekend? }
end

Lovely,
maybe it worth to put startdate as second argument, because when it is
placed in the first you cannot omit it

···

2015-03-09 3:58 GMT+10:00 botp <botpena@gmail.com>:

On Thu, Feb 26, 2015 at 11:46 AM, Arup Rakshit > <aruprakshit@rocketmail.com> wrote:
> I'll be looking for some other solutions to the problem. :slight_smile:
>

you can try collect then select, eg,

$cat weekdays.rb

require 'date'

class Date
  def weekend?
    saturday? || sunday?
  end
end

def weekdays startdate=Date.today, ffdays = 7
  (startdate..(startdate+ffdays)).select{|d| !d.weekend? }
end

if $0 == __FILE__
  w=weekdays.map{|d| d.to_s+" "+Date::DAYNAMES[d.wday]}
  puts "#{(p w).size} weekdays"
end

$ruby weekdays.rb
["2015-03-09 Monday", "2015-03-10 Tuesday", "2015-03-11 Wednesday",
"2015-03-12 Thursday", "2015-03-13 Friday", "2015-03-16 Monday"]
6 weekdays

That's what I did. Here's my suggestion:

require 'date'

def weekdays(n = 5, start = Date.today)
  .tap do |result|
    while result.size < n
      while start.saturday? || start.sunday?
       start = start.succ
      end

      result << start
      start = start.succ
    end
  end
end

Not the shortest version but I think it's pretty readable.

Kind regards

robert

···

On Mon, Mar 9, 2015 at 2:54 AM, Dmitry Shvetsov <shvetsovdm@gmail.com> wrote:

def weekdays startdate=Date.today, ffdays = 7
  (startdate..(startdate+ffdays)).select{|d| !d.weekend? }
end

Lovely,
maybe it worth to put startdate as second argument, because when it is
placed in the first you cannot omit it

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can -
without end}
http://blog.rubybestpractices.com/

i'd probly prefer a lone loop, like eg

while result.size < n
      unless start.saturday? || start.sunday?
          result << start
      end

      start = start.succ
end

best regards
--botp

···

On Mon, Mar 9, 2015 at 9:20 PM, Robert Klemme <shortcutter@googlemail.com> wrote:

    while result.size < n
      while start.saturday? || start.sunday?
       start = start.succ
      end

      result << start
      start = start.succ
    end

Yep, good point! The redundant start = start.succ annoyed me as well.

Kind regards

robert

···

On Mon, Mar 9, 2015 at 7:27 PM, botp <botpena@gmail.com> wrote:

On Mon, Mar 9, 2015 at 9:20 PM, Robert Klemme > <shortcutter@googlemail.com> wrote:
> while result.size < n
> while start.saturday? || start.sunday?
> start = start.succ
> end
>
> result << start
> start = start.succ
> end

i'd probly prefer a lone loop, like eg

while result.size < n
      unless start.saturday? || start.sunday?
          result << start
      end

      start = start.succ
end

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can -
without end}
http://blog.rubybestpractices.com/

We could make it a bit shorter while still (or so I think) retaining
readability:

def weekdays(n = 5, start = Date.today)
  .tap do |result|
    while result.size < n
      result << start unless start.saturday? || start.sunday?
      start = start.succ
    end
  end
end

Now, that's a solution I like. :slight_smile:

Cheers

robert

···

On Tue, Mar 10, 2015 at 8:10 AM, Robert Klemme <shortcutter@googlemail.com> wrote:

On Mon, Mar 9, 2015 at 7:27 PM, botp <botpena@gmail.com> wrote:

On Mon, Mar 9, 2015 at 9:20 PM, Robert Klemme >> <shortcutter@googlemail.com> wrote:
> while result.size < n
> while start.saturday? || start.sunday?
> start = start.succ
> end
>
> result << start
> start = start.succ
> end

i'd probly prefer a lone loop, like eg

while result.size < n
      unless start.saturday? || start.sunday?
          result << start
      end

      start = start.succ
end

Yep, good point! The redundant start = start.succ annoyed me as well.

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can -
without end}
http://blog.rubybestpractices.com/

+1
indeed. ruby's elegance does scale.
eg, we can stretch it to use blocks, eg

    weekdays_excluding{|day| day.saturday? || day.sunday?}

wc, after a sec thought, bounces me back to ruby's idomatic chunky
style (wc in turn reminde me of _whytheluckystiff), iow, back to

    weekdays.select{|day| !(day.saturday? || day.sunday?)}

or

    weekdays.delete_if{|day| day.saturday? || day.sunday? }

oh well : )
best regards --botp

···

On Tue, Mar 10, 2015 at 3:11 PM, Robert Klemme <shortcutter@googlemail.com> wrote:

      result << start unless start.saturday? || start.sunday?
Now, that's a solution I like. :slight_smile:

Or use the block not as a filter criteria but to process dates:

def weekdays(n = 5, start = Date.today)
  return to_enum(:weekdays, n, start) unless block_given?

  while n > 0
    unless start.saturday? || start.sunday?
      n -= 1
      yield start
    end

    start = start.succ
  end
end

I think I prefer that solution because you can still get your Array via
weekdays.to_a and yet do not have to materialize. This is especially
convenient if n is large.

Kind regards

robert

···

On Wed, Mar 11, 2015 at 6:57 AM, botp <botpena@gmail.com> wrote:

On Tue, Mar 10, 2015 at 3:11 PM, Robert Klemme > <shortcutter@googlemail.com> wrote:
> result << start unless start.saturday? || start.sunday?
> Now, that's a solution I like. :slight_smile:
>

+1
indeed. ruby's elegance does scale.
eg, we can stretch it to use blocks, eg

    weekdays_excluding{|day| day.saturday? || day.sunday?}

wc, after a sec thought, bounces me back to ruby's idomatic chunky
style (wc in turn reminde me of _whytheluckystiff), iow, back to

    weekdays.select{|day| !(day.saturday? || day.sunday?)}

or

    weekdays.delete_if{|day| day.saturday? || day.sunday? }

oh well : )
best regards --botp

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can -
without end}
http://blog.rubybestpractices.com/

Well, I just came up with this (slightly boring) solution:

  require 'date'

  def weekdays n, start=nil
    date = start || Date.today
    result = []

    loop do
      return result if result.size >= n
      result << date unless date.cwday > 5
      date = date.succ
    end
  end

it could easily be modified to accept a block and yield (or return an enum)
if that's what you want.

I don't know why I made a new local variable, I think I just like the
clarity.

···

--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

you are spot on as always.

but i go on a different route re coding. i dont know why. but that is
just my style maybe.
so if i would to address the op's problem, i would just plainly return
a range of days. this gives me a quick start on coding, like

  def weekdays(n = 5, start = Date.today)
    start..start+n
  end

then if am concern on processing large number of date values, i could
then "lazy" it up, like

  weekdays.lazy

then i can select wc days i want, eg

  weekdays.lazy.select{|d| !(d.saturday? || d.sunday?)}

and then probably process it

  weekdays.lazy.select{|d| !(d.saturday? || d.sunday?)}.map{|d|
d.to_s+" "+Date::DAYNAMES[d.wday]}

then i could probably process that further w #each, or stop on #force
or #to_a, etc, etc.

in a sense, my brain find it easy to process logic by chaining things
one at a time... but then again, it's just for my own simple brain :
)

best regards
--botp

···

On Wed, Mar 11, 2015 at 4:00 PM, Robert Klemme <shortcutter@googlemail.com> wrote:

I think I prefer that solution because you can still get your Array via
weekdays.to_a and yet do not have to materialize. This is especially
convenient if n is large.

That makes sense. The endless loop does have the disadvantage though
because it clouds the intention a bit here.

Also, there is a slight semantic difference: since you do not use
Date.today as default argument this code will not catch clients that
wrongly passed nil or false as value of "start". Whether that is desirable
is another question but it's a difference to just do

def weekdays n, start = Date.today
  date = start
...
end

Kind regards

robert

···

On Wed, Mar 11, 2015 at 11:42 AM, Matthew Kerwin <matthew@kerwin.net.au> wrote:

Well, I just came up with this (slightly boring) solution:

  require 'date'

  def weekdays n, start=nil
    date = start || Date.today
    result =

    loop do
      return result if result.size >= n
      result << date unless date.cwday > 5
      date = date.succ
    end
  end

it could easily be modified to accept a block and yield (or return an
enum) if that's what you want.

I don't know why I made a new local variable, I think I just like the
clarity.

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can -
without end}
http://blog.rubybestpractices.com/

> I think I prefer that solution because you can still get your Array via
> weekdays.to_a and yet do not have to materialize. This is especially
> convenient if n is large.

you are spot on as always.

Thank you!

but i go on a different route re coding. i dont know why. but that is
just my style maybe.
so if i would to address the op's problem, i would just plainly return
a range of days. this gives me a quick start on coding, like

  def weekdays(n = 5, start = Date.today)
    start..start+n
  end

But for that you do not really need a method, do you?

then if am concern on processing large number of date values, i could

then "lazy" it up, like

  weekdays.lazy

Oh, I wasn't aware of this. Thank you for the education!

then i can select wc days i want, eg

  weekdays.lazy.select{|d| !(d.saturday? || d.sunday?)}

and then probably process it

  weekdays.lazy.select{|d| !(d.saturday? || d.sunday?)}.map{|d|
d.to_s+" "+Date::DAYNAMES[d.wday]}

then i could probably process that further w #each, or stop on #force
or #to_a, etc, etc.

For me it feels more natural to start with the generator and turn it into
something non lazy on demand (i.e. via #to_a) than first having something
potentially large and turning it into something lazy. It seems more
efficient (at least memory wise) to start with the small generator than
with something else (i.e. you can even invoke #lazy on an Array where you
do have all the values already).

in a sense, my brain find it easy to process logic by chaining things
one at a time... but then again, it's just for my own simple brain :
)

I think all our brains are that way (or at least most, you never know). :slight_smile:

Kind regards

robert

···

On Wed, Mar 11, 2015 at 6:20 PM, botp <botpena@gmail.com> wrote:

On Wed, Mar 11, 2015 at 4:00 PM, Robert Klemme > <shortcutter@googlemail.com> wrote:

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can -
without end}
http://blog.rubybestpractices.com/