Welcome!

Kurt Cagle

Subscribe to Kurt Cagle: eMailAlertsEmail Alerts
Get Kurt Cagle via: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: RIA Developer's Journal, AJAX World RIA Conference, JavaScript

RIA & Ajax: Article

Real-World AJAX Book Preview: Counting on JavaScript Arrays

Real-World AJAX Book Preview: Counting on JavaScript Arrays

This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs for the special pre-order price, click here for more information. Aimed at everyone from enterprise developers to self-taught scripters, Real-World AJAX: Secrets of the Masters is the perfect book for anyone who wants to start developing AJAX applications.

Counting on JavaScript Arrays
Objects (and object functions) are remarkably useful things, but there are times when all you're really concerned about is a list of items. List manipulation can be found at the heart of any number of sophisticated languages so it's probably not surprising to discover that JavaScript actually has quite a powerful toolset of array capabilities.

The JavaScript Array object is, as the name suggests, a means of defining arrays or linear lists. Unlike most languages, a JavaScript list doesn't have any specific restrictions put on the contents of the list - such an array can hold strings, numbers, objects, functions (yep, even functions), or some combination thereof.

There are a number of different ways that you can create an array. The most formal is using the Array() object:

var arr = new Array();

which creates a new empty array. The constructor can similarly be used with a sequence of items:

var colors = new Array("red","orange","yellow","green","blue","violet");

which will create an array with the seven primary colors of the spectrum.

Similarly, you can create an array using the array operator "[]":

var arr = [];
// This creates an empty array.
var colors = ["red","orange","yellow","green","blue","violet"];
// colors

Either way once you define an array, you access it by providing the position of the array item you want to recover, with item 0 being the first item so referenced:

var color = colors[0];
print(color);
      "red"

In most versions of JavaScript, so long as either the "new Array" or the left square bracket is on the same line as the variable assignment, you can spread your arrays across multiple lines for easier legibility:

var colors = ["red",
              "orange",
              "yellow",
                  "green",
                  "blue",
                  "violet"];
                  // colors

While in the case of single word items this may seem to be a bit of overkill, if the objects in question were objects or functions, it could make an otherwise hideously complex expression at least marginally more readable.

Arrays can, as this suggests, contain objects that are more complicated, including objects, functions, and even other arrays. For instance, you could create a set of points in a path as follows:

var seq = [[0,0],
              [1,0],
              [1,1],
              [0,1],
              [0,0]
];

To access the third point in this sequence, you'd write:

seq[2];

and the second item of the third point as:

seq [2][1];

Of course, in a situation like this it might be easier to work with associative objects:

var seq = [{x:0,y:0},
              {x:1,y:0},
              {x:1,y:1},
              {x:0,y:1},
              {x:0,y:0}];

Then you could access the y value of the third points as:

Print(seq[2].y);
=> 1

Similarly, you can define an object class with appropriate constructors:

var Pt = function(ax,by){
      this.x = ax;
      this.y = by;
      this.toString = function(){
         return "("+this.x + "," + this.y+")";
         }
      }
var seq = [new Pt(0,0),new Pt(1,0),new Pt(1,1),new Pt(0,1),new Pt(0,0)];
print(seq[2].y);
=> 1
Print(seq[3]["x"]);
=> 0

Note that evaluation proceeds from left to right - the first coordinate set describes the point, the second the sub-point.

Enumerating through an array is surprisingly simple, largely because it's nearly identical in process to that of enumerating through an object's properties. The for (key in arr) command will do the actual iteration and put the index in the variable key:

for (colorIndex in colors){ print (colorIndex+": " +colors[colorIndex]); }
=>
0: red
1: orange
2: yellow
3: green
4: blue
5: violet

If you don't need the specific index (you're only concerned about the data in the array), you can also use the for each (item in arr) command:

for each (color in colors){ print(color); }
=>
red
orange
yellow
green
blue
violet

Finally, you can manually iterate over the dataset (especially in cases where the newer for/in or for each/in syntax is available):

for (var colorIndex = 0;colorIndex != colors.length; colorIndex++){
      var color = colors[colorIndex];
      print (color);
      }
=>
red
orange
yellow
green
blue
violet

where the length property returns the number of items in the array.

I've found over the years that the number of uses I have for internally predefined arrays is generally far smaller than the uses I have for dynamic arrays - either arrays that are generated by other processes or arrays that can be made via the "stack" methods that the modern Array object exposes.

Two of these primary methods are push() and pop(). The push method appends a value to the end of the array and is equivalent to setting an index to the array length, then assigning the value to this new array cell:

Array.prototype.push = function(x){this[this.length] = x; return this.length;}

Note: The prototypes that are described here show how these methods can be defined on older JavaScript implementations (most notably Internet Explorer). In general, for the methods of an object such as Array, you should use the conditional test below to create an alternative implementation.

if (Array.prototype.push == null){
    Array.prototype.push = function(x){
       this[this.length] = x;
       return this.length;
       }
    }

The pop() method, on the other hand, removes the last item from the array and reduces the size of the array by one, returning the removed item in the process. Its signature is similar:

Array.prototype.pop = function(){
      var result = this[this.length - 1];
      this[this.length - 1] = null;
      this.length--;
      return result;
      }

Note that push() and pop() work on the array itself, not on a copy. Two other functions that work on arrays directly are the reverse() method and the sort() method. Reverse, as expected, simply reverses the order of the array from its existing sequence:

print(colors.reverse());
=>
violet
blue
green
yellow
orange
red
print(colors.reverse())
=>
red
orange
yellow
green
blue
violet

Sort is a little more complex. If applied to an array without an argument, it will sort the list alphabetically. However, it also basically punts when objects of a more complex type (such as Objects, functions, arrays, etc.) are passed, just returning the objects in hash order (typically the order that they were defined).

However, being able to control the characteristics of a sort are especially useful with objects. For instance, suppose you had a person object that consisted of both first and last name. You could control how the objects are sorted by using sort callback functions. A sort callback function is a function that takes two arguments and returns the following values depending on the relationship of the arguments:

  • If a should be less than b, then return -1 (any negative number),
  • If a should be greater than b, then return 1 (any positive number), and
  • If a and b are considered equal, then return 0.
For instance, consider the Person class:

var Person = function(_firstName,_lastName){
    this.firstName = _firstName;
    this.lastName = _lastName;
    this.sortFirst = function(a,b){
       if (a.firstName < b.firstName){return -1;}
       if (a.firstName >b.firstName){return 1;}
       return 0;
       }
    this.sortLast = function(a,b){
       if (a.lastName < b.lastName){return -1;}
       if (a.lastName >b.lastName){return 1;}
         return 0;
         }
       this.toString = function(){return this.firstName+" "+this.lastName;}
       }

You can then choose to sort an array of persons by first or last name:

var persons = [];
persons.push(new Person("Kurt","Cagle"));
persons.push(new Person("Alice","Delamare"));
persons.push(new Person("Edward","Eagleton"));
persons.push(new Person("Laura","Wood"));
persons.sort(persons[0].sortFirst);
=> Alice Delamare,Edward Eagleton,Kurt Cagle,Laura Wood
persons.sort(persons[0].sortLast);
=> Kurt Cagle,Alice Delamare,Edward Eagleton,Laura Wood

The use of callback functions also plays a role in the filter() and map() functions (both part of JavaScript 1.6 and available natively in Mozilla, but creatable from other methods as well).

The filter() method iterates through all items in a list, applying a callback filter function. If the function returns true for the given item, then the item will be included in the resulting list, otherwise it won't.

For instance, suppose you wanted to retrieve all colors that were one to five characters in length. With the filter defined, you can easily iterate through the colors list:

var shortFn = function(val){return val.length < 6;}
colors.filter(shortFn)
=>red,green,blue

The filter() method has the following prototype definition:

Array.prototype.filter = function(callbackFn){
      var resArray = [];
      for each (value in this){
         if (callbackFn(value)){
             resArray.push(value);
             }
       }
      return resArray;
      }

The map() method, on the other hand, provides a convenient way of doing a single operation on an array to generate a new array. For instance, suppose you wanted to convert the first letter of every color in a list of colors to upper case. You can do this as follows:

var uc = function(val){
      return val[0].toUpperCase()+val.substr(1);
      }
print(colors.map(uc));
=> Red,Orange,Yellow,Green,Blue,Violet

The map() method has the following prototype definition:

Array.prototype.map = function(callbackFn){
      var resArray = [];
     for each (value in this){
         resArray.push(callbackFn(value));
         }
      return resArray;
      }

One final method that's not defined in any JavaScript implementation is the peek() method. The peek() method is like the pop() method in that it returns the last element in a list, but unlike pop() it doesn't remove this element. It's primarily useful for working with lists as stacks. While it's trivial to implement, by working with arrays as if they are stacks, you can create a number of interesting algorithms:

Array.prototype.peek = function(){
      if (this.length > 0){
    return this[this.length - 1];
    }
      else {
         return null;
         }
      }
    print(colors.peek());
    "violet"

Strings form the next major set of primitives, both directly and through the use of specialized kinds of objects called Regular Expressions. While languages such as C++ treat strings as arrays of characters, JavaScript works with an explicit String object.

This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs, click here to order.

More Stories By Kurt Cagle

Kurt Cagle is a developer and author, with nearly 20 books to his name and several dozen articles. He writes about Web technologies, open source, Java, and .NET programming issues. He has also worked with Microsoft and others to develop white papers on these technologies. He is the owner of Cagle Communications and a co-author of Real-World AJAX: Secrets of the Masters (SYS-CON books, 2006).

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.