This Week in JavaScript - 7 October 2013

This is a weekly update of some interesting things relating to JavaScript, to help encourage discussion and to bring some potentially new ideas to the fore.

Here’s what we’ve seen of interest this week.

Quiz

You Can’t JavaScript Under Pressure - Five functions to fill. One ticking clock. How fast can you code?
If I’m completely honest, I needed 17 minutes and five seconds. Who can beat that? (shouldn’t be hard :)).

Free Book

JavaScript Allongé by Reginald Braithwaite is a free book (to read online) that focuses on using functions to write simpler JavaScript with fewer bugs

Listening

Chales Max Wood and Joe Eames talk about working from home on the most recent JavaScript Jabber podcast.
Does anyone reading this work from home? I do, and I love it!

Code Quality

JSHint just got a makeover.
What do you think? Better, worse?

Libraries

One Page Scroll - Create an Apple-like one page scroller website (iPhone 5S website) with the One Page Scroll plugin
Top 5 jQuery UI Alternatives

Debugging

The JavaScript console API - An overview of the various methods of the console.

Take a look at these recent happenings in JavaScript, let us know what you think about them, and we’ll have some more to inspire you next week.

Feel free to PM me or [URL=“http://www.sitepoint.com/forums/private.php?do=newpm&u=158377”]PM Paul if you have anything interesting for the next issue.

Oops! The Free book does not appear to be free :frowning: (unless you want to read it on-line!)
Cheers, Dave

Thanks for pointing that out, Dave. I amended my post accordingly to reflect that fact.
It’s still a pretty good deal, though :slight_smile:

Has anyone tried You Can’t JavaScript Under Pressure yet?

How did you go?
Did you (like me) make a couple of runs at it?

Hehe, I’m still going. It’s up to about 56 hours now. :smiley:

Well, not being a programmer doesn’t help … and I hate the timer thing, but it’s still fun to have a go and learn something. Pullo has also been coaching me along on the sly, too. I’m up to question 4. :slight_smile:

Only because in a fit of clumsiness I refreshed my browser half-way through. DOH!

For me the main sticking point was question 5.

I’m still helping Ralph with one of these questions (so don’t want to give the answer away), but once I’ve heard back from him maybe we can compare answers / techniques.

Hehe, I’m still going. It’s up to about 56 hours now.

I had a go and managed the first 4 (after a bit of googling) but stuck in a recursive loop on question 5 at the moment.:slight_smile:

It took me " 178 minutes, 37 seconds for all 5 levels. Well done! "

But that did include walking the dog, having lunch, a little bit of work and vast amounts of googling :slight_smile:

I’m not sure my last answer was good coding though so if someone wants to tidy it up and point out the errors it would be appreciated.

//test5
var total=0;
function arraySum(i) {

// i will be an array, containing integers, strings and/or arrays like itself.
// Sum all the integers you find, anywhere in the nest of arrays.
 for (var loop= 0; loop < i.length; loop++)
 {
  // check if it is an array
				 if (i[loop] instanceof Array) {
					var 	nested = i[loop];
					arraySum(nested);
					}
  // check if a number
  if (typeof i[loop] == "number"){     
   total= total + i[loop];
  }

}
return total;
}

Hi Paul,

That’s exactly how I did it, except that you can eliminate the need for the global variable like so:

[spoiler]function arraySum(i) {
var total = 0;
for (var loop= 0; loop < i.length; loop++){
// check if it is an array
if (i[loop] instanceof Array) {
var nested = i[loop];
total += arraySum(nested);
}

	// check if a number
	if (typeof i[loop] == "number"){ 
		total += i[loop];
	}
} 
return total;

}[/spoiler]

I only had to check Google for the syntax of one particular method, so I was quite pleased with that.
I’ll post the rest of my answers later and we can compare :slight_smile:

I always have trouble with global variables :slight_smile:

It took me a while to work out what you had done here:

total += arraySum(nested);

But I finally realised that the array was returning the value of the total so it wasn’t lost when it was set to zero at the beginning of the function.

These were my answers for tests 2 - 4:

//test 2
function isNumberEven(i) {
// i will be an integer. Return true if it’s even, and false if it isn’t.
return (i%2 == 0) ? true : false;
}
// test 3
function getFileExtension(i) {
// i will be a string, but it may not have a file extension.
// return the file extension (with no period) if it has one, otherwise false
var has_dot = false;
has_dot = i.indexOf(‘.’);
if (has_dot > -1){
return i.substr(i.lastIndexOf(‘.’) + 1)
}
return false;
}
// test 3
function getFileExtension(i) {
// i will be a string, but it may not have a file extension.
// return the file extension (with no period) if it has one, otherwise false
var has_dot = false;
has_dot = i.indexOf(‘.’);
if (has_dot > -1){
return i.substr(i.lastIndexOf(‘.’) + 1)
}
return false;
}
//test4
function longestString(i) {
// i will be an array.
// return the longest string in the array
var longest = 0;
var longstring = 0;
for (var l=0; l < i.length; l++)
{
if (i[l].length > longest && typeof i[l] == “string”) {
longest = i[l].length;
longstring = l;
}
}
return i[longstring];
}

Any tips, improvements or suggestions welcomed :slight_smile:

I ended up having syntax issues when using typeof and blanked on instanceof, so a different solution was used by me.
Now that I know that they do work, I’ll take another run at things later.

So, the first two are no brainers:

function doubleInteger(i) {
  // i will be an integer. Double it and return it.
  return i*2;
}
function isNumberEven(i) {
  // i will be an integer. Return true if it's even, and false if it isn't.
  return i%2 === 0;   
}

With three I still had the “under pressure” bit in my mind, so I just went with what I knew:

function getFileExtension(i) {
  // i will be a string, but it may not have a file extension.
  // return the file extension (with no period) if it has one, otherwise false
    
  var regEx = /.*\\.(.+)/;
  if (i.match(regEx, "")){
    return i.replace(regEx, "");
  } else {
    return false;
  }
}

Which can be tidied up a bit:

function getFileExtension(i) {
  var match = /.*\\.(.+)/.exec(i)
  return (match)? match[1] : false;
}

Number four looked like this:

function longestString(i) {
  // i will be an array.
  // return the longest string in the array

  var j,
      len = i.length,
      longest = "";
		
  for(j = 0; j < len; j++){
    if(typeof i[j] === "string"){
      if(i[j].length > longest.length){
        longest = i[j]
      }
    }
  }
}

However, last week Paul linked to a useful article abut filtering arrays, so I also tried to employ that:

function longestString(i) {
  return i.filter(function (el) {
    return (typeof el === "string");
  }).sort(function(a, b){
    return b.length - a.length; 
  })[0];   
}

Which basically uses Array#filter to get all of the sting elements into an array, sorts the new array and returns the first element.

With number five, like you, I started with a global variable, but that nagged at me, so I ended up with this:

function arraySum(i) {
  // i will be an array, containing integers, strings and/or arrays like itself.
  // Sum all the integers you find, anywhere in the nest of arrays.

  var sum = 0,
      j, 
      len = i.length;
  
  for (j = 0; j < len; j++){
    if (i[j] instanceof Array) {
      sum += arraySum(i[j]);
    }

    if (typeof i[j] === "number"){ 
      sum += i[j];
    }
  } 
  return sum;
}

What got me here is that I was expecting to be able to test for an array with typeof i[j] === "array" which didn’t work.

@Pullo;

Happy to see you’ve switched from equality == to identity === in your latest examples. We could be comparing to “0” using equality in @Paul_O_B; #2, for example, and due to type conversion, it will still work, but it’s not advisable.

A suggestion for #3:


function getFileExtension(i) {

    // i will be a string, but it may not have a file extension.
    // return the file extension (with no period) if it has one, otherwise false
    var extension = i.split(".").pop();
    return ( extension !== i ) ? extension : false;

}

Here I’m caching the result of string splitting, and get the last element, using Array.prototype.pop(). If the last (and only) element is the string itself, there was no splitting involved, so we return false.

Ok thanks :slight_smile:

From reading a little it seems that its safer to always use “===” rather than “==”.

You got it! :slight_smile: MDN is the best JS resource for me: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators

For #4 and #5 actually functional programming design patterns is what comes to mind.

#4
@Pullo; uses Array filter, one of the new additions to ecmascript. Unfortunately, it’s not quite mainstream, but the underscore.js library is a good use here.

We first reject from the array the non-string elements (first filter), and then max the longest string out (second filter): http://jsbin.com/OkOGIxI/2/edit
Hide HTML and Output, show Console and hit run.

#5
Very similar to #4, except we flatten the array first, reject non-numbers this time, and reduce it to sum: http://jsbin.com/OkOGIxI/4/edit

Functional programming design patterns bring clarity, and once the new Array methods become mainstream (once IE8 goes extinct), we can drop underscore.js. Until then, I’m glad this little gem of a library exists.

Classic. Array is Object. Use toString in Object’s prototype:


Object.prototype.toString.call( i[j] ).toString() === '[object Array]'

Or:

if ( Array.isArray(i[j] ) {

Doesn’t work on IE < 9, but it passes the test.

Hi @myty ;

Thanks for all the tips!
I really like your solution for number 5 and will be looking more closely at underscore.js from now on.

You’re welcome @Pullo;.

And it’s a toss out with underscore.js. You can also check out lodash.js and lazy.js

Those were what caused me fits as well, plus the fact that the values passed on a recursive array must be byRef instead of byVal in javascript…

Took me 12 minutes total, which included time stopped to discuss work issues, some language differences (C# vs javascript), plus a couple boneheaded problems on my part…