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? :)
Considerations
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) {
bar.doSomething(el);
} else {
bar.doSomethingElse(el);
}
Assumption #1: doSomething and
doSomethingElse are truly named like that and this is the answer thinking in a non abstract way:
bar["doSomething"+(foo?"":"Else")](el);
Assumption #2: the method name is just indicative:
bar[foo?"doSomething":"doSomethingElse"](el);
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';
console.log(foo);
})();
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?
foo.bar = foo.bar; // 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
foo.bar = foo.bar; I have written already)
Thinger.prototype.bar = "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
foo.bar = bim.bar = "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 foo.bar;
delete bim.bar;
Thinger.prototype.bar = "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
_instances.push(this);
}
// the public magic method
CrazyStuff.defineInstancesProperty = function (name, value) {
_instances.forEach(deleteProperty, name);
this.prototype[name] = value;
};
// some chained property ...
CrazyStuff.prototype.bar = "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--; ) fn.call(context, this[i]);
};
return CrazyStuff;
}());
var
foo = new CrazyStuff,
bim = new CrazyStuff
;
alert(foo.bar); // buz
foo.bar = "overwritten";
alert(foo.bar); // overwritten
CrazyStuff.defineInstancesProperty("bar", function () {
alert("YO, even methods!");
});
foo.bar(); // 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 () {
console.log(this.bar);
}
};
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
instanceOfThinger.hasOwnProperty("bar");
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:
Object.prototype.hasOwnProperty.call(object, "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" &&
obj[key].destroy();
// the optional leaks paranoic way
delete obj[key];
}
}
destroyThemAll(myObjects);
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 availableOh 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');
$(this).width('100px');
});
$('.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.click(function() {
// $el refers to 1 element
$el.attr('title', 'new title');
$el.width('100px');
});
$el.click();
});
Then of course
Assumption #2 has been
jQuery oriented
// assuming jQuery
$(document).ready(function() {
var $el = $('.foo #bar')
.css({
color: "red",
border: "1px solid shit"
})
.text("new text")
.click(function () {
$el
.attr('title', 'new title')
.width('100px')
;
})
.click()
;
});
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;
dojo.xhrGet({
url : 'foo.php',
load : function(resp) {
foo = 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;
dojo.xhrGet({
url : 'foo.php',
load : function(resp) {
// assuming foo is reused
// no need to assign otherwise
if (foo = resp.foo) {
// run important code
}
}
});
})();
8: how could you rewrite the following code to make it shorter?
(function(d, $){
$('li.foo a').attr('title', 'i am foo');
$('li.bar 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
["foo","bar","baz","bop"].forEach(function(c){
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++) {
$('#thinger').append('i am thinger ' + i + '
');
$('#gizmo').append('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.
var
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;
};
}(parseFloat));
// makes every integer negative
var parseInt = (function (parseInt) {
return function (i, base) {
i = parseInt(i, base);
return i < 0 ? i : -i;
};
}(parseInt));
// negative abs
Math.abs = (function (abs) {
return function (n) {
return -abs(n);
};
}(Math.abs));
// 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 = menuItems.map(function (item) {
return item.name + (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"
say('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:
...
12/32
12/33
12/34
...
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 areOK, 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(', '));
Conclusion
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.
UpdateThat was fast and ...
I did not pass the test!
Alex S
wrote the reason, anticipating Rebecca thoughts, fair enough :)