Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Sequences using JavaScript Array

DZone's Guide to

Sequences using JavaScript Array

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

numbers

Generating a sequence is a common programming task. This is rather easy to achieve using a straightforward loop. With JavaScript however, there exists a more functional variant using the powerful Array object. This permits the generation of all kind of sequences, from A..Z to a list of prime numbers, without any loops at all.

Supposed you are about to create a list of numbers 1, 2, 3 and place it in an array, one possible loop-based implementation is:

var result = [];
for (var i = 1; i != 4; ++i) result.push(i)
console.log(result);  // [1, 2, 3]

Obviously, tweaking the code can yield a different kind of sequence. For example, a sequence of Latin alphabets is a matter of converting each number into the right letter:

var list = '';
for (var i = 0; i != 26; ++i) list += String.fromCharCode(i + 65);
console.log(list);   // 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

Using loops is nice, but can we get the same result sans loops? Fortunately, JavaScript is quite capable of doing it. We just need to rely on its built-in object Array. In the following explanation, all the sections mentioned refer to the Standard ECMA-262, the official ECMAScript Language Specification edition 5.1.

Earth

First of all, we need to create an array which has the specified amount of elements. For that 1,2,3 example, we need a 3-element array. Fortunately, this is trivial:

Array(3);

Note that there is no need to use new for object construction, this is explained in Section 15.4.1:

When Array is called as a function rather than as a constructor, it creates and initialises a new Array object.

Array(3) creates an array with the length of 3, it is the same as [,,,]. The resulting array however has holes in it. In fact, although it has 3 elements, those elements do not exist. Holes like this are not so obvious if you try to peek at the array contents. Compare the two lines:

Array(3).join('-');                // "--"
[null,undefined,null].join('-');   // "--"

We can verify the existence of an array element just like checking for a property in a generic JavaScript object, using the in operator (Section 11.8.7):

0 in Array(3);   // false
1 in Array(3);   // false
2 in Array(3);   // false
2 in [,,9];      // true

As pointed by Axel Rauschmayer, holes inside an array are also detected with forEach.

Water

How to fill those holes? A trick discovered by many seasoned JavaScript developers (see e.g. Brandon Benvie’s post on es-discuss) is to use Array.apply. Instead of some empty elements, now we have undefined to replace them:

Array(3);                  // [,,,]
Array.apply(0, Array(3));  // [undefined, undefined, undefined]

To really understand this trick, recall how Function.prototype.apply works (Section 15.3.4.3), particularly in the Step 8, transforming an array into an argument list for the function, called spreading. No wonder this approach is quite often used to find the minimum or maximum value in an array. In the following fragment, the two lines are identical:

Math.max(14, 3, 77);                 // 77
Math.max.apply(Math, [14, 3, 77]);   // 77

When apply receives an array with an empty element, it gets converted into undefined and thereby eliminates any holes in the array. If we combined it with Array construction, the end effect is constructing a new array with the spread.

Array.apply(0, [1,,3]);  // is the same as
Array(1, undefined, 3);

Air

Now that we have an array with the right number of elements, how do fill it with the right sequence? Array.prototype.map to the rescue! Section 15.4.4.19 shows that:

map calls callbackfn once for each element in the array, in ascending order, and constructs a new Array from the results.

Further down, we also observe that:

callbackfn should be a function that accepts three arguments. callbackfn is called with three arguments: the value of the element, the index of the element, and the object being traversed.

The second argument, the index of the element, is the key to our solution:

Array.apply(0, Array(3)).map(function (x, y) { return y + 1; });  // [1, 2, 3]

And if the sequence is about the squares of the first few integers:

Array.apply(0, Array(3)).map(function (x, y) { return (y + 1) * (y + 1); });

Finally, for the English alphabets ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’:

Array.apply(0, Array(26)).map(function(x,y) {
  return String.fromCharCode(y + 65);
}).join('');

For alternatives to using map, see also Brandon Benvie’s usage of Function.call with Number or Ben Alman’s solution with Object.keys.

Fire

With the new array comprehension feature of the forthcoming ECMASCript 6, the above one-liners can be slightly tweaked. For example, the alphabets sequence might be written as (note the use of arrow function):

[for (i of Array.apply(0, Array(26)).map((x, y) => y))
String.fromCharCode(65 + i)].join('');

Here the conversion from each number to the corresponding letter is carried out outside the map callback. This makes the code easier to digest, it resembles a proper composition: generate the sequence first and do another step to transform the sequence. For details, check out my previous blog post (with tons of examples) on ECMAScript 6 and Array Comprehension.

Loops are overrated, aren’t they?

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:

Published at DZone with permission of Ariya Hidayat, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}