My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Wednesday, September 15, 2010

The Rebecca Murphey's Challenge

What is the best/easiest way to understand if a {{ProgrammingLanguage}} developer is a {{ProgrammingLanguage}} dev and not simply a CV bloater?

A Technical Test

Nothing simpler than this, and this is what @rmurphey has recently done with a nice JavaScript challenge published on GitHub.
Unfortunately in some country (Italy as example) IT is still a matter of piece of paper for whatever {{ProgrammingLanguage}}, even those completely unrelated with the generic developer academic course/program, as often happens with JavaScript, the most used one and probably the less considered one at the same time ( ... hopefully it comes after VB ... )
Anyway, Rebecca comes from North Carolina and we all know in USA "they know something about IT", don't we? :)

Developers Rumors

I have probably arrived too late, but apparently lots of developers more updated than me knew about this test ... and this is good.
When developers are happy to be tested they demonstrate already to be brave, convinced about their knowledge, and ready for new challenges and I am pretty sure Rebecca will thank them all.
About me? I am just "challenges addicted" so even if I am not interested in Rebecca proposal, I could not avoid this test and this post aim is to describe question after question, pretending I would have applied ... are we ready? :)


JavaScript is extremely flexible and expressive so there is rarely a universal solution for a single problem. Moreover, I did not know Rebecca and I have read nothing about this test when I have tried to consider that some answer may be redundant while I have re-thought some other to provide a better answer. Finally, to solve the test it's strictly necessary to make a lot of assumptions since questions are often too poor. Let's start now!

1: how could you rewrite the following to make it shorter?

if (foo) {
} else {

Assumption #1: doSomething and doSomethingElse are truly named like that and this is the answer thinking in a non abstract way:


Assumption #2: the method name is just indicative:


Latest approach could be optimized if the same operation should be repeated after and using two strings references for method names so that minifiers can play well.

2: what is the faulty logic in the following code?

var foo = 'hello';

(function() {
var foo = foo || 'world';

Do you remember my good old/shorter way to address the "undefined" string to a local variable?

var u = typeof u;

The logic is the same, but Rebecca does not know me so I think I should explain that once we declare local scope variables these have prevalence against the outer scope and a function scope is always invoked resolving in advance function declarations and knowing already declared scope variables.

(function () {

alert([onFnCall, notAfter]);

// tons of JavaScript and ...

for (var onFnCall = 1; 0;);
function notAfter(){}

alert([onFnCall, notAfter]);


The first alert will be undefined, function notAfter(){} while the second one will show the onFnCall assigned value as well.
What is important to know is that no error occurs on the first alert and it will always be undefined, even if there is an outer scope variable with the same name.
I have an old IE related post about this, have fun :)

3: given the following code, demonstrate you know JS objects

var Thinger = function() {
return this;

Thinger.prototype = {
bar : 'baz'

var foo = new Thinger(),
bim = new Thinger();

The first thought after the very first line of code has been something like: Dude, you don't like the non (IE) standard but unbelievably helpful "name" property, don't ya? Neither you like saved bytes

function Thinger() {
return this;

The instant second thought on the instant second line has been something like: "... neither you present good example! Why on earth that bloody redundant and pointless return this which is worldwide recognized as implicit?"

function Thinger() {}

So now, accordingly with the rest of the code, I can at least go on reading it ...

3.1: how would you override the value of the bar property for the variable foo without affecting the value of the bar property for the variable bim? =; // it's enough

The moment we assign a property to a generic instanceof Object is the moment we are sure that obj.hasOwnProperty("directly assigned") will return true, even if the assignment is for an undefined value.
Fair enough, the moment I execute latest line of code, is the moment I ask foo to access its prototype chain and assign the returned value, if any, to the property with the same name. Is bim affected by this operation? Not at all!

3.2: how would you affect the value of the bar property for both foo and bim?

Assumption #1: we are talking about the initial case, without operations in the middle (e.g. the =; I have written already) = "via chained properties";

Any JS developer knows that objects properties access look up for the chained prototype, where if this has no property but still a __proto__, the lookup will continue until we reach the null __proto__:

Object.prototype.__proto__ == null;

In this case life is easy, there is only one chain and the lookup stops immediatly.

Assumption #2: objects creation and the moment we would like to be sure both objects "bar" properties are changed could be performed after any sort of code, included the one posted at the beginning.
With latest assumption we have two options, the most obvious one:

// sub assumption, NO ES5 and defineProperty = = "overwrite";

While the second one, still considering no ES5 in place where Object.defineProperty could avoid such operation, is the safer equivalent of the first one:

delete; = "via chained properties";

Latter approach could be used to change shared runtime properties or methods in those constructors that would like to track each instance, e.g.

var CrazyStuff = (function () {

// the constructor
function CrazyStuff() {
// with a register

// the public magic method
CrazyStuff.defineInstancesProperty = function (name, value) {
_instances.forEach(deleteProperty, name);
this.prototype[name] = value;

// some chained property ... = "buz";

// some prototype method ...
CrazyStuff.prototype.destroy = function () {
var i = _instances.indexOf(this);
if (-1 < i) {
_instances.splice(i, 1);

// the public magic method helper
function deleteProperty(instance) {
delete instance[this];

// instances container
var _instances = [];

// ensure indexOf method (quick version)
_instances.indexOf = _instances.indexOf || function (value) {
for (var i = this.length; i--; ) {
if (this[i] === value) break;
return i;

// same with forEach (quick version)
_instances.forEach = _instances.forEach || function (fn, context) {
for (var i = this.length; i--; ), this[i]);

return CrazyStuff;


foo = new CrazyStuff,
bim = new CrazyStuff

alert(; // buz = "overwritten";
alert(; // overwritten

CrazyStuff.defineInstancesProperty("bar", function () {
alert("YO, even methods!");
});; // YO, even methods!

As I have said before and as you can see, it's never that easy to answer a generic JS question without thinking or creating different ways to obtain similar results.

3.3: how would you add a method to foo and bim to console.log the value of each object's bar property?

Surely avoiding runtime changes to the prototype, even if I have just showed some CrazyStuff.
I am not sure if the question is appositely wrongly formulated, but the only answer I could provide locally was changing the initial example:

Thinger.prototype = {
bar : 'baz',
logBar: function () {

It does not matter if Rebecca meant something else, I have already demonstrated how easy is to change runtime properties (and then methods) and I would never use the same technique for a simple method.
The Assumption, in any case, is that there is a public object called console and that this has a log method, otherwise the first instanceOfThinger.logBar() call will produce an Error.

3.4: how would you tell if the object's bar property had been overridden for the particular object?

At least in three ways, considering that ...
Assumption #1: no fools man, too easy


If you don't know hasOwnProperty method, I am pretty sure you have failed the whole test and you are not into JavaScript at all (yet). It is still probably worth it to keep reading this post to hopefully learn something new :)

The Assumption #2 is that we are talking about generic objects. In this case we cannot be sure that hasOwnProperty method is inherited from the Object.prototype (e.g. host objects) so, as safer solution, I would do something like:, "bar");

The last Assumption #3 is that I am a wannabe, but I am not a surrender, since in the Ninja code there is no space for loosers:

// optimistic, still ignorant, attempt
function hasOwnProperty(obj, name) {
var result = false, value;
if (name in obj) {
value = obj[name];
delete obj[name];
result = !(name in obj && obj[name] === value);
if (result) {
obj[name] = value;
return result;

hasOwnProperty(foo, "bar");

Latest attempt will easily fail if both instance and the generic prototypeOf have a property with the same value.

4: given the following code, destroy recursively

var myObjects = {
thinger : new myApp.Thinger(),
gizmo : new myApp.Gizmo(),
widget : new myApp.Widget()

Once again, to many assumptions to consider here. It's not specified if the environment is clean, or sandboxed, as it's not specified if myObjects life cycle ends up after the operation. To be sure about almost everything, here the code:

function destroyThemAll(obj) {

for(var key in obj) {
// the "PrototypeJS in da house" way
// if (myObjects.hasOwnProperty(key))
// slower and completely pointless
// for a "destroy" operation
// all we want is to call destroy
// if present
obj[key].destroy && obj[key].destroy();

// alternative:
// paranoid way, ensuring nobody
// attached a destroy property that
// is not a function
typeof obj[key].destroy == "function" &&

// the optional leaks paranoic way
delete obj[key];



I am sure you got the fact I hate when problems lack of description details :)

5: given the following array, loop over it and do stuff

var myArray = [ 'foo', 'bar', 'baz' ];

This question has basically no answer since it's not specified what we would like to reach. Is it performances? is it readability, is it both? The only point clear is:
you can assume the library of your choice is available
Oh really? Dear Rebecca, I would seriously avoid to hire any developers that is not able to solve this problem without a library ... seriously!!!
In any case, I don't even need to comment this question since Morgan Roderick already created a test page where almost all possible solutions are in place, included my performances oriented in revision 25.
Despite those examples, one of my first attempt was to use the classic trick to repeat strings, instantly screwed up by that space in the middle.

// fast string repeat
function str_repeat(str, times) {
return new Array(++times).join(str);

str_repeat("a", 3); // aaa

6: how could you improve the following code?

$(document).ready(function() {
$('.foo #bar').css('color', 'red');
$('.foo #bar').css('border', '1px solid blue');
$('.foo #bar').text('new text!');
$('.foo #bar').click(function() {
$(this).attr('title', 'new title');

$('.foo #bar').click();

Here we are again, while I am sure somebody wrote the $.each loop for the precedent question, in this one it's not even mentioned that the library is jQuery ... so why should I think in a jQuery optimization way?
Even if my name is for some reason inside jQuery source code, my first thought, without knowing Rebecca and the fact she is into "jQuery world", has been something like: "dude, the dollar function has been showed via PrototypeJS before jQuery and has been used or re-invented from every developer, me included ... the chainability may be optional, you know that, don't ya ..."
So, this was Assumption #1: dollar does not mean jQuery and chainability.

// non jQuery way
$(document).ready(function() {
// cache the result once
var $el = $('.foo #bar');
// no chainability
// no knowledge of
// methods signature
// the example worked?
// this should work as well
$el.css('color', 'red');
$el.css('border', '1px solid blue');
$el.text('new text!');
$ {
// $el refers to 1 element
$el.attr('title', 'new title');

Then of course Assumption #2 has been jQuery oriented

// assuming jQuery
$(document).ready(function() {
var $el = $('.foo #bar')
color: "red",
border: "1px solid shit"
.text("new text")
.click(function () {
.attr('title', 'new title')

There is a sub assumption in latter case, the $el acts only to the first element of the CSS query, if not the $(this) inside click() is still necessary.
Now, you know what? Since Rebecca forgot to specify that jQuery was the library, I did not bother myself to check the API ... sorry if it's wrong, but the question is to me incomplete.

7: what issues do you see with the following code? how would you fix it?

(function() {
var foo;

url : 'foo.php',
load : function(resp) {
foo =;

if (foo) {
// run this important code

Let's assume I am a framework user and I know that dojo is a framework with a xhrGet method ... the problem may be that the call is Asynchronous.
I say "may be" 'cause I am sure somebody could have changed the default behavior for whatever reason so ... Rebecca, are you looking for framework users or JavaScripters?
Anyway ...

(function() {
var foo;

url : 'foo.php',
load : function(resp) {
// assuming foo is reused
// no need to assign otherwise
if (foo = {
// run important code


8: how could you rewrite the following code to make it shorter?

(function(d, $){
$(' a').attr('title', 'i am foo');
$(' a').attr('title', 'i am bar');
$('li.baz a').attr('title', 'i am baz');
$('li.bop a').attr('title', 'i am bop');
})(dojo, dojo.query);

Shorter? Here I am:

// new lines for blog readability
dojo.query("li."+c+" a").attr("title","i am "+c)

But since I have assumed that I could have used any library, even if 90% of the time all we need is just a bit deeper knowledge of JS to obtain the same faster result, it must be written instantly after the IE version:

for(var a=["foo","bar","baz","bop"],i=a.length;i--;)
dojo.query("li."+a[i]+" a").attr("title","i am "+a[i])

Shortest ever, isn't it? But maybe Rebecca would have liked to know that dojo argument was completely redundant, considering that dojo.query does not use this internally as dojo reference, otherwise the whole thing would not have runned at all?

9: how would you improve the following code?

for (i = 0; i <= 100; i++) {

i am thinger ' + i + '


i am gizmo ' + i + '


I really would like another Morgan like page for this test, assuming we are still talking about performances.
The number one thing to do is to avoid DOM manipulation inside loops, whatever library we are using. So, for sure, append should be moved after the loop, then we can talk about performances.

i = 0,
re = /\{name\}/g, // ES5 friendly
str = new Array(102).join(

i am {name} {i}

).replace(/\{i\}/g, function () {
return i++;
$('#thinger').append(str.replace(re, "thinger"));
$('#gizmo').append(str.replace(re, "gizmo"));

In my attempt I don't even use a loop at all but append() a part, I really would like to compare different ways to create those 2 similar strings ... maybe I should create one ...

10: a user enters their desired tip into a text box; the baseTotal, tax, and fee values are provided by the application. Find issues ...

function calculateTotal(baseTotal, tip, tax, fee) {
return baseTotal + tip + tax + fee;

This question has been the most ridiculous one I have found in this test since it is a complete nonsense.
First of all, the number of assumptions are unlimited, nothing is clear and ... the design is crap!
Seriously, why on bloody earth I would create a function that accepts 3 variables from an abstract "application" that I suppose I should trust (should i?) and the only argument provided anyhow from the user is ... the second one?
calculateTotal(fromApp1, fromUser, fromApp2, fromApp3) ... I don't really know what you think, but this is one of the crappiest signature I have ever seen ... so Rebecca, please don't be mad at me, but can I rewrite the whole thingy?

10++: a user enters their desired tip into a text box that is gonna be summed with the rest of spent money. Write a function able to return the sum of the user tip, provided via input.value, and a generic number, this time provided by the application.

Now we talk ...
Since I do believe this task is about security, I am quite sure few developers provided this kind of solution for the tip parsing:

function getTotal(tip, amount) {
// cast float compatible
// without function calls
tip *= 1;
if (
// isNaN is not reliable
// what we know by specs
// is that NaN != NaN ... so
tip != tip ||
// if tip is negative
// the user is trying
// to fuck the bill!
tip < 0
) {
// in both cases
// we don't want to go on ..
throw "invalid tip";
// muda-h-acker !!!
return amount + tip;

If it comes to JavaScript and user exposed runtime changeable code, the word security is a sort of joke.
Security must be on server side, but it is surely true we can help the latter one improving client side security as much as possible.
So, why I have created that function?

// makes every number negative
var parseFloat = (function (parseFloat) {
return function (f) {
f = parseFloat(f);
return f < 0 ? f : -f;

// makes every integer negative
var parseInt = (function (parseInt) {
return function (i, base) {
i = parseInt(i, base);
return i < 0 ? i : -i;

// negative abs
Math.abs = (function (abs) {
return function (n) {
return -abs(n);

// always false
var isNaN = function () {
return false;

I could go on forever so ... we don't want to trust anymore online forms, do we?
Even worse, if we put the secure logic inside a closure, we could always find the user with an older Firefox version able to reach the private scope and change things there ... scary, uh? Now try to make my attempt fail, since not a single call is changeable (expect the function itself via older Firefox and the scope, private or not).
Repeat with me: security is never enough and on client side it's almost absent :P

11: given the following data structure, do stuff with properties

var menuItems = [
id : 1,
name : 'Salad',
extras : [
'Chicken', 'Steak', 'Shrimp'

id : 2,
name : 'Potato',
extras : [
'Bacon', 'Sour Cream', 'Shrimp'

id : 3,
name : 'Sandwich',
extras : [
'Turkey', 'Bacon'

id : 4,
name : 'Bread'

We can assume any library we like ... and that's why I have chosen JavaScript, you gotta love it :D

var result = (item) {
return + (item.extras ? " (" + item.extras.join(", ") + ")" : "");

Too easy task at number 11, I feel like I have missed something here ... benchmarks Morgan? :)

BONUS 1: write code such that the following alerts "Hello World"


... am I missing something here?

// solution one
// the easy one
var say = function (hello) {
return function (world) {
alert(hello + " " + world);

// solution two
// the less memory one
var say = function (content) {
if (say.content) {
alert(say.content + " " + content);
content = "";
say.content = content;
return say;

BONUS 2: what is the faulty logic in the following code? how would you fix it?

var date = new Date(),
day = date.getDate(),
month = date.getMonth(),
dates = [];

for (var i = 0; i <= 5; i++) {
dates.push(month + '/' + (day + i));

console.log('The next five days are ', dates.join(', '));

At my first try I did a mistake ... I stopped reading inside the for loop!
If like me you have database and server side experience, including Ajax/ActionScript calendars manipulation, you know which one is the first problem ever with dates: we cannot sum days or months that easily.
That's it, whatever this task asks, the first date I have thought, being the day incremented, has been the new year, aka: 31st December.

In few words, every end of the month above loop will populate the dates array with values like:


Does that Date make any sense? No it does not, no reason to investigate more ... the logic is already crap.
Let's fix it:

// incomplete!
for (var i = 0; i <= 5; i++) {
date.setDate(day + i);
if (month != date.getMonth()) {
day = -i;
month = date.getMonth();
dates.push(month + '/' + (day + i));

However, another thing we should always remember is that months and days, unless properly formatted, are almost always from 0 to 11, for months, and 0 to 30 for day of the month.
This means that while the day can be increased, the month should be showed properly adding the classic plus one.
This is the first part of the problem, and I am pretty sure the reason this challenge has been placed online at the beginning of the month is not a coincidence, nice trap Rebecca ;)

If we keep reading we can spot something else we could consider as faulty logic:
The next five days are
OK, so we have a loop that goes from 0 to 5 included, which means 6 values: 0,1,2,3,4,5
We can already spot there is something wrong here, so we either fix the string writing "next six days" ... or we simply fix the loop.
However, we should be carefully, because if we talk about next days, the current one should NOT be included.
This is the proper solution for this last task:

var date = new Date,
day = date.getDate(),
month = date.getMonth(),
dates = [];

// next 5 days
for (var i = 1; i < 6; i++) {
// set the next day
date.setDate(day + i);
// verify that the month did not change
if (month != date.getMonth()) {
// if it did, set day as -i
// removing from "i" the loop start: 1
day = -(i - 1);
// re assign the month
month = date.getMonth();
// month + 1 AND day + i
dates.push((month + 1) + '/' + (day + i));

console.log('The next five days are ', dates.join(', '));


The description of a problem/task is fundamental, no discussions about it. We all know sometimes our customers don't even know what they want ... it's true, but if we do not insist asking details, anything could become a massive mess. A single detail could make the difference, the real goal is even more important ... in this case we had to guess a lot, make assumptions, spend more time than necessary to be sure our "customer", in this case Rebecca, would have been satisfied.
It does not matter if Rebecca will ever consider us, the thing that matters is that we learn to take things carefully and we remember that 2 similar solutions, logically speaking, could have completely different performances impact in a more complex/structured application.
We should chose carefully, analyzing implications, browser features, security model, and whatever is necessary to do our task as good as possible and our goal will be already closer.

I hope you have appreciated this post, maybe learned something or, even better, corrected me where I have done wrong, and also suggestions are welcome :)

Last, but not least, I do hope Rebecca will spend some time to read this post and "judge me" properly but AFAIK she gonna write about this test tomorrow so stay tuned in her blog.

That was fast and ... I did not pass the test!
Alex S wrote the reason, anticipating Rebecca thoughts, fair enough :)


Alex S said...

I think a lot of the value of the original quiz, was that it wasn't defined exactly for the same results, and it had some ambiguity in it. I'm not sure which clients that you have worked with, but none of them let me know whether I'm going for speed or memory management.

Also, you got angry at her for assuming a framework in some cases. While it's obviously silly to have a framework for simple little questions like this, the work that she is likely looking to hire someone for is much more complex, and uses a framework. It is wise of her to test people's knowledge of these frameworks.

You have exquisite detailed answers, but you've effectively berated the client, and they'd likely fire you. You failed the business case.

I understand that you took this as somewhat of a JavaScript Quiz, but the _clear_ intention wasn't to find out someones precise ability to find holes in the questions. It was to find someone's ability to work well alongside her team. This was not a Kangax JS Test, this was a set of interview questions.

Further, if she can't assume a framework is available, why do you get to decide to use the not-always-compatible array map function? Seems like the same issue.

Anyways, I appreciate the technical knowledge of this post, but ultimately feel like you just plain missed the point.

Andrea Giammarchi said...

Alex S really valid points and apparently Rebecca already discarded me :D

The problem with quizzes, specially with technical ones, is that the "candidate" would never disturb or ask too much to avoid feeling not prepared or not good enough for the role ... but why should the "candidate" be abandoned due lack of info or, in the worst case scenario, in front of a quiz written by somebody that did some mistake?

I am sure Rebecca put a lot of things on purpose, but I still wonder why, being the gist way something "cold", without any interaction, something that does not provide any extra answer and all the candidate would like to do is to do it properly.

You are right there is no "customer" able to tell you details but being there means you know the company or what the company is doing ... you are already oriented for that kind of role.

Which one is the role here? A JS chap? Somebody that knows different libraries? It's not clear, can I ask this face to face? I cannot!

About the business case, I simply try to be myself as much as possible and since this is the blog where I rant 99% of the time against everything, I thought Rebecca wouldn't have been offended and, if so, my apologies, I was simply evaluating the test from a *non* *interested* point of view, as I have found it on the road 'cause somebody left it there (nobody for clarifications, a lonely piece of paper with questions).

Further, if she can't assume a framework is available, why do you get to decide to use the not-always-compatible array map function? Seems like the same issue.
Not really, for me vice-versa offers many functionalities PrototypeJS does, and I consider it my JavaScript framework where is there, as it would be if I chose another one.

Anyways, I appreciate the technical knowledge of this post, but ultimately feel like you just plain missed the point.
Probably yes, and thanks for your comment.
Thanks Rebecca as well that commented via twitter.


Anonymous said...

Oh my, another one of those tests/quizzes. Yawn... Still thanks for your detailed answer :)

Peter van der Zee said...

#3 I prefer anonymous function expressions to named expressions or even declarations. They prevent hoisting for one and also possibly prevent legacy implementation fuckups with named expressions. And you'll never make the mistake (yes, mistake) of a function declaration inside another statement :)

__proto__ and the likes are not standard. Asside from the complexities of understanding prototype vs [[Prototype]], they're simply not guaranteed to work. Do not "need" them.

#3.3 overwrites the entire prototype object, which can hardly be desired. But you probably know this.

#4 is clearly just asking for a loop that destroys, where you may assume destroy exists (so why check anyways?).

#5 fails for anything that's not a string. Array.prototype.concat.apply([],{ return [x,x,x];})); is one of undoubtfully many approaches.

#7 it does not matter what framework xhrGet belongs to. All that matters is whether xhrGet will call the load method of the object passed on _before_ it returns. Also, your fix does not necessarily fix the problem because you don't know what "important stuff" does. Maybe it's an init-once kind of thing ;)

#9 that's improvement? I'd rather have the original... or maybe something like the following. Readibility is key.

for (i = 0; i <= 100; i++) {
var pre = '< p>< span class="';
var mid = '">i am ';
var post = ' '+i+'';


#10 dude... the answer is simply that the + operator is overloaded. If the user input is not converted to number the result is a string, period. Otherwise the result is a number, NaN (okay, technically still a number). I don't think this was about security at all.

I think I'm with Rebecca on this one, you kind of missed the point :)

Andrea Giammarchi said...

You missed the point of almost al my answers so we agree on disagree.

#3 nothing to worry about in my shoed example ... what you prefer is unconditional choice while I prefer to chose the best way accordingly with what I need for that task.

__proto__ was a simplified description, not part of the solution indeed but simply part of the explanation. Why bother about it?

#3.3 all about examples, you probably know this as well

#4 you are right but I have described possible assumptions for a more abstract destroy

#5 fails for what? on #4 you say I don't need to check and now you invented a test that does not exist? Have you checked the link I have provided?

#7 again ... ASSUMPTIONS? Thanks to underline again the whole point I made with this post :-)

#9... Readibility is key ... where is it written? The very first question is about making it less readable ... and you decided that is the key for the 9th?
You simply confirmed one more time what I have said, you have to make lots of assumptions ... I start thinking you did not read my post at all, you rather went through examples and nothing else ...

#10 that is your answer ( easy level ) mine goes into details ... sorry you screwed up this one ;-)

Anonymous said...

Seems to be more readable and requires less comments then yours :)

var date = +new Date,
timeSpan = 1000*60*60*24, // milliseonds in the day
dates = [];

for (var i = 1; i <= 5; i++) {
var nextDate = new Date(date + i * timeSpan);
dates.push(nextDate.getMonth() +1 + '/' + nextDate.getDate());

console.log('The next five days are ', dates.join(', '));

Andrea Giammarchi said...

nice one ... but dude, you are late :D