Wednesday, January 31, 2007

The Story of business_days(...)

I've been really busy with work recently. However, while going over my requirements for the nth time, something caught my eye: business days. In particular, finding the date 7 business days from a another date.

Far from being a hard task, it fell more into the annoying category. Or maybe it would make for a good interview question. In the end, how complicated or simple can someone make such a function?

A little background first. I had to use JavaScript, a language I'm only superficially familiar with. Also, after discussion with the customer, the definition of business days was Monday to Friday. No holidays are included... In fact, which holidays? And from what region/country?

Having flirted with Haskell recently and being "influenced" by the SICP lectures, a few ideas came to mind.

"business days" is a subset of "days", and both are infinitely lists. If I have a list of all the days and I filter it through the is_business_day(date) function, and I access the element at position 7, I get the answer. I was getting pretty angry with my inability to accomplish my task in such a straightforward way. I started to understand what people mean when they talk about the expressiveness of a programming language.

I then turned to Ruby for ideas. The succ function came to mind, I made that my starting point.
function include(value, list) {
for (var i=0; i < list.length; i++) {
if(list[i] == value) return true;
};
return false;
}

function succ(date) {
return new Date(date.getTime() + 1000 * 60 * 60 * 24);
}

function is_business_day(date) {
return include(date.getDay(), [1,2,3,4,5]); // mon-fri
}

function business_succ(date) {
var result = succ(date);
return is_business_day(result) ? result : business_succ(result);
}

function repeat(f, x, n) {
if(n == 1)
return f(x);

return f(repeat(f, x, n-1));
}

function days(date, n) {
return repeat(succ, date, n);
}

function business_days(date, n) {
return repeat(business_succ, date, n);
}
Personally, the moment of enlightenment came when I realized that iterating for days or business days was the same code and that it could be made even more generic by the repeat function.

A few comments:
  • the include function would usually be borrowed from prototype.js

  • recursion has become part of my day-to-day programming techniques

  • all functional

  • definitely not the shortest code I've written, but all pieces are both trivial and reusable

  • a definite LISP-like flavor

  • proof that SICP and friends have been damaging my brain :)

4 Comments:

Blogger chebuctonian said...

I like it! The easy way would have been to hard-code the values. (M->W, Tu->Th, W->F, Th->M, F->Tu). That's not terribly re-usable though.

After playing with prototype a bit more, I decided to see what I could do. It's nothing really usable right now, and I'm still fighting javascript. Ah well... you may be interested in
http://www.prototypejs.org/api/enumerable
http://www.prototypejs.org/api/array

The first to go would be the include function, which is already in the array prototype, so you could do:
[1,2,3,4,5].include(date.getDay())

My only beef naming-wise is "succ" which I don't find terribly readable.

It would be nifty making this whole code better integrated with prototype. Gary?

7:31 AM  
Blogger Jonathan said...

Did I forget to mention that I also needed this function for numbers other than 7? :D

wrt prototype.js, I invite you to re-read my first comment following the code. I coded it myself here for the sake of completeness.

Of course, 'succ' comes straight from Ruby. To get the benefits of ranges, all you need to implement is 'succ'

8:05 AM  
Blogger Gary Haran said...

It's funny I haven't seen the SCIP lectures yet, though I have been playing with prototype and scriptaculous an awful lot.

This post really tells me I should be downloading the lectures.

9:26 AM  
Blogger chebuctonian said...

Aaah, right, I should have my green tea in the morning before replying to blog posts ;)

Would you extend the Date prototype and give them a "succ" method? (The name still seems crufty, and historical cruft is still bad imnsho)

Gary: those lectures are definitely great. Speaking of which, that's just the kick in the butt I needed to finish them.

6:06 PM  

Post a Comment

<< Home