Chapter 3. Working with Arrays and Functions

Introduction

An array is an ordered collection of elements. In JavaScript, an array can be created using formal object notation, or it can be initialized using literal notation, as demonstrated in the following code:

var arrObject = new Array("val1", "val2"); // array as object
var arrLiteral = ["val1", "val2"]; // array literal

To the developer, there is no difference: you can invoke an Array method on both a literal and an object. However, to the JavaScript engine, an array literal has to be reinterpreted each time it’s accessed, especially when used in a function call. Array literals can, however, replace the need for temporary variables, especially when sending values to a function.

Speaking of which, JavaScript functions provide a way to encapsulate a block of code in order to reuse the code several times. They’re typically created using the function statement and syntax similar to the following:

function functionname(arg1, arg2, ..., argn) {
   function body
}

JavaScript supports a Function object, which can be constructed the same as a String or Number, using the new operator:

var fn = new Function (arg1, arg2, ..., argn, functionbody);

However, using this syntax is not as efficient as using the function statement, because using a function constructor requires that the function be parsed each time it’s called. Functions defined with the function statement are parsed once, when the code is loaded.

There are three basic kinds of functions:

Declarative function
A declarative function is a statement triggered by the use of the function keyword, and parsed when the JavaScript application is first loaded.
Anonymous function or function constructor
An anonymous function is constructed using the new operator and referencing the Function object. It’s anonymous because it isn’t given a name, and access to the function occurs through a variable or another object property. It’s parsed each time it’s accessed.
Function literal or function expression
As with other JavaScript objects, functions can be both object and literal. A literal function is a function expression, including parameter and body, which is used in place—such as in an argument to another function. Like a declarative function, it’s also parsed only once, when the JavaScript application is loaded. Like the function created as an object, it can also be anonymous.

Looping Through an Array

Problem

You want to easily access all elements of an array.

Solution

The most common approach to accessing an array is to use a for loop:

var mammals = new Array("cat","dog","human","whale","seal");
var animalString = "";
for (var i = 0; i < mammals. length; i++) {
   animalString += mammals[i] + " ";
}
console.log(animalString);

Discussion

A for loop can be used to access every element of an array. The array begins at zero, and the array property length is used to set the loop end.

Sometimes, though, you don’t want to access every element of the array. For instance, you might want to traverse an array until you find either a specific element, or any element that meets (or doesn’t meet) a certain criteria. In these cases, you’ll want to use a while loop and test the array elements:

var numArray = new Array(1,4,66,123,240,444,555);
var i = 0;

while (numArray[i] < 100) {
     console.log (numArray[i++] > 50);
}

Notice that the index counter, i, is incremented as it’s used to access an array element. The use of i++ means that the existing value of i is accessed first, and then the variable is incremented.

Creating a Multidimensional Array

Problem

You want to create a multidimensional array (an array of arrays).

Solution

Create an array in which each element is also an array. For example, to create an array with three elements, each of which is also an array of three elements containing, respectively, string, number, and array literals, you can use the JavaScript in Example 3-1.

Example 3-1. Creating a multidimensional array
// set array length
var arrayLength = 3;

// create array
var multiArray = new Array(arrayLength);
for (var i = 0; i < multiArray.length; i++) {
  multiArray[i] = new Array(arrayLength);
}

// add items to first array index
multiArray[0][0] = "apple";
multiArray[0][1] = "banana";
multiArray[0][2] = "cherry";

// second
multiArray[1][0] = 2;
multiArray[1][1] = 56;
multiArray[1][2] = 83;

// third
multiArray[2][0] = ['test','again'];
multiArray[2][1] = ['Java','script'];
multiArray[2][2] = ['read','books'];

console.log(multiArray); // printed out in first index order
console.log(multiArray[2]); // prints out subarray
console.log(multiArray[2][2][0]); // individual item

As the example also demonstrates, JavaScript array elements don’t have to be the same data type, and may, themselves, be objects, such as another array.

Discussion

Multidimensional arrays in JavaScript are managed by creating a new array as an element within an existing array. The new array can be created as an Array element, or as an array literal.

In Example 3-1, an array, multiArray, is created as an Array object with three members. Each of those three elements is also created as Array objects with three members. The array data is then set, with the first array member containing string literals, the second containing number literals, and the third containing array literals—themselves containing two array members, each with a string literal.

To access the array elements, use the square bracket notation, with each set of brackets used to address each level of the array. In the following code, the array contents are output, after being converted to a string first, if necessary:

alert(multiArray[2]); // prints out test,again,Java,script,read,books
alert(multiArray[2][2]); // prints out read,books
alert(multiArray[2][2][1]); // prints out books

Multidimensional arrays are typically used to hold the data from a table structure, but how the structure is maintained is up to the developer. For instance, the developer can support an array structure in which the outer index reflects the columns, and the inner reflects the rows. As an example, Table 3-1 shows a simple five-column, three-row table containing a set of numbers.

Table 3-1. Simple table with five columns and three rows and sample data

45.89

4

34

9998.99

56

3

23

99

43

2

1

1

0

43

67

To create this in JavaScript using a multidimensional array, use the following code:

var table = new Array(5);

table[0] = [45.89, 4, 34, 9998.99, 56]; // first row
table[1] = [3, 23, 99, 43, 2]; // second row
table[2] = [1, 1, 0, 43, 67]; // third row

Of course, this doesn’t take into account column and row headers. To add in the headers, just treat them as array data, making sure to incorporate them into the proper place in the array to reflect the table structure.

In a multideveloper environment, it’s essential that there is agreement among the developers about whether table structure data is stored column-centric or row-centric.

Creating a String from an Array

Problem

You want to create a single string from an array.

Solution

Use the Array object’s built-in join method to join the array elements into a string:

var fruitArray = ['apple','peach','lemon','lime'];
var resultString = fruitArray.join('-'); // apple-peach-lemon-lime

Discussion

The Array join method takes one optional parameter, a delimiter used to separate the array element contents when joined—in this case, the dash (-). It returns a string with all of the array elements concatenated. If the array contains anything other than strings, the values are converted to a string equivalent:

var numberArray = [1,2,3,4,5]; // array literal containing number elements
var resultString = numberArray.join('+'); // returns string with 1+2+3+4+5

If the array element isn’t a string, it’s converted to a string before concatenation:

var objArray = [['a','b','c'],13,'apple', new Date()];
resultString = objArray.join('***');

If the delimiter parameter isn’t provided, a comma is inserted between array element values by default:

var numberArray = [1,2,3,4,5];
var resultString = numberArray.join(); // returns string with 1,2,3,4,5

Sorting an Array

Problem

You want to sort an array.

Solution

Use the Array object’s sort method:

var fruitArray = ['strawberry','apple','orange','banana','lime'];
console.log(fruitArray.sort()); // returns apple,banana,lime,
                             orange,strawberry

Discussion

The Array object’s sort method sorts the array elements alphabetically if no optional compare function parameter is provided. To facilitate the sort, all data types are converted to their string equivalent before sorting:

var numberArray = [4,13,2,31,5];
console.log(numberArray.sort()); // returns  13,2,31,4,5

Though the array members in this example are numbers, they’re sorted in lexicographical (dictionary) order, not numerically. To do an actual numeric sort, use a custom sort function:

function compareNumbers(a,b) {
   return a - b;
}
var numArray = [13,2,31,4,5];
console.log(numArray.sort(compareNumbers)); // prints 2,4,5,13,31

The function subtracts the second parameter value from the first, and if the first is less than the second, a negative value is returned; otherwise, the value is positive. If the return value is less than zero, the sort index for the second parameter is set higher than the first parameter. If the value is greater than zero, the sort index for the first parameter is set higher than the other. If the value is exactly zero, the sort index for the two is unchanged.

If the array elements contain strings that could be converted to numbers, then the compareNumbers sort function still works, as string-to-number conversion is handled transparently:

var numberArray=["34","4","5"];
console.log(numberArray.sort(compareNumbers)); // prints 4,5,34

The sort method sorts the elements in an ascending order. If you want to do a reverse sort, use the sort method to sort the elements, and then use the reverse method to reverse the array member order:

var numberArray = [4,5,1,3,2];
numberArray.sort();
numberArray.reverse(); // array now has 5,4,3,2,1

Create a New Array as a Subset of an Existing Array

Problem

You want to create a new array from a segment of an existing array. If the array elements are objects, you want to keep both arrays in sync.

Solution

Use the Array object slice method to create a new array based on elements within a given range:

var origArray = new Array(4);
origArray[0] = new Array("one","two");
origArray[1] = new Array("three","four");
origArray[2] = new Array("five","six");
origArray[3] = new Array("seven","eight");

// create new array using slice
var newArray = origArray.slice(1,3);

Discussion

The Array slice method is a simple way of building a new array from a consecutive sequence of elements in another array. The parameters are the beginning and ending index for the sequence of elements to copy. A negative value for either index indicates that slice should work from the end of the array.

If the copied elements are literal values, such as strings, numbers, and Booleans, they’re copied _by value_—changing the value in the old array has no impact on the same values in the new array, and vice versa.

When objects are copied, though, they’re copied by reference, whether they’re copied via slice or by direct variable assignment:

var first = new Array("one","two","three");
var second = first; // copied by reference
second[1] = "apple"; // first and second arrays now have "one","apple","three"

The code that follows demonstrates the object syncing when used with slice. A section of one array is used to create a new array with slice. The elements in the first array are Array objects. In the code, when the value of one of the objects in the first array is changed, the change is reflected in the new array. Conversely, when a value is changed in the new array, the change is reflected in the original array:

var origArray = new Array(4);
origArray[0] = new Array("one","two");
origArray[1] = new Array("three","four");
origArray[2] = new Array("five","six");
origArray[3] = new Array("seven","eight");

var newArray = origArray.slice(1,3);
alert(newArray); // prints out three,four,five,six

// modify original
origArray[1][0] = "octopus";

// print out new
console.log(newArray); // prints out octopus,four,five,six

// modify new
newArray[1][1] = "kitten";

// print out one, two, octopus, four, five, kitten,
// seven, eight
console.log(origArray);

Another handy use for slice is to convert the function arguments property into a proper array:

var args = Array.prototype.slice.call(arguments);

Using slice to create a subset of an array is a way of quickly copying a subset of an array and, if the values are objects, ensure both arrays are in sync.

Searching Through an Array

Problem

You want to search an array for a specific value and get the array element index if found.

Solution

Use the Array object methods indexOf and lastIndexOf:

var animals = new Array("dog","cat","seal","elephant","walrus","lion");
console.log(animals.indexOf("elephant")); // outputs 3

Discussion

Though support for both indexOf and lastIndexOf has existed in browsers for some time, it’s only been formalized with the release of ECMAScript 5. Both methods take a search value, which is then compared to every element in the array. If the value is found, both return an index representing the array element. If the value is not found, –1 is returned. The indexOf method returns the first one found, the lastIndexOf returns the last one found:

var animals = new Array("dog","cat","seal","walrus","lion", "cat");

console.log(animals.indexOf("cat")); // prints 1
console.log(animals.lastIndexOf("cat")); // prints 5

Both methods can take a starting index, setting where the search is going to start:

var animals = new Array("dog","cat","seal","walrus","lion", "cat");

console.log(animals.indexOf("cat",2)); // prints 5
console.log(animals.lastIndexOf("cat",4)); // prints 1

Creating a Block of Reusable Code

Problem

You want to create a block of code that you can use several times.

Solution

Create a simple, named, parameter-less function using the function statement:

function simpleFunction() {
   console.log("Hello, function!");
};

simpleFunction();

Discussion

A function created using the function keyword and given a name is known as both a declarative function and a static function. The basic structure is:

function functionName() {
   // JavaScript statements
}

This type of function is parsed when the page containing the JavaScript application is loaded, and the parsed results are used whenever the function name is referenced. It’s an efficient way of reusing the same code.

Any name that would be valid for a variable would work for a function. Variable names can be any combination of characters, numbers, and underscores, as long as the variable name starts with a character or underscore and case-sensitivity is preserved.

However, functions typically perform some action, and best practices suggest that the function name should be descriptive. For instance, a function that sums the numbers in an HTML table might be named sumTableValues.

Passing Single Data Values to and from a Function

Problem

You need to pass data values into a named function and get a result back.

Solution

Provide arguments for the incoming data, and return the result:

function makeHello(strName) {
   return ("Hello " + strName);
}

window.onload=function() {
   var name = prompt("What's your name?","");
   var greeting = makeHello(name);
   alert(greeting);
}

Discussion

Function arguments are a way to pass data to a function. The arguments are separated by commas and included in the parentheses following the function name:

var firstName = "Some":
var lastName = "Person";

makeHello(firstName, lastName);

The function then processes the arguments, as needed:

function makeHello(firstName, lastName) {
    alert("Hello " + firstName + " " + lastName);
}

Data is returned from the function to the calling program using the return statement:

function makeHello(firstName, lastName) {
   return "Hello " + firstName + " " + lastName;
}

You can pass several arguments to the function, but only one return value. If you want to return more than one value, you can pass more complex objects, such as an array, to and from a function.

Unless you’re sure about the type of data coming from the user, you’ll want to also test the data first. For instance, if you want to ensure that a value passed to the function is a number, you could do the following:

function someFunc(num) {
   if (typeof num == "number") {
      ...
   }
}

You also don’t have to provide a one-to-one mapping between the value passed to the object, and the number of function parameters. Accessible from within a function is an arguments object that contains all of the arguments passed to the function. It’s not a true array, but you can use array index notation to access the argument, and it does provide a length property for the number of arguments:

function sumNums() {
  var sum = 0;
  for (var i = 0; i < arguments.length; i++) {
     var num = parseFloat(arguments[i]);
     if (!isNaN(num)) {
        sum+=num;
     }
   }
   return sum;
}

var sum = sumNums(4.55, 3.0,1, "apple", 56.33);

See Also

“Passing Complex Data Objects to a Function” demonstrates passing more complex objects as arguments.

Passing Complex Data Objects to a Function

Problem

You need to pass more complex data to a function.

Solution

You can use objects, such as arrays, as function arguments:

function makeHello(name) {
   name[name.length] = "Hello " + name[0] + " " + name[1];
}
var name = new Array('Ima','Reader');
makeHello(name);
alert(name[2]); // displays "Hello Ima Reader"

Discussion

Function arguments in JavaScript can be simple scalar values, such strings or numbers, or they can be complex objects, such as arrays. Arrays are a simple way to pass many values to a function without having to create separate parameters for each. It’s also a way to pass an unknown number of values to a function, such as an array of numbers to be summed:

function addNumbers(nums) {
   var total = 0;
   for (var i = 0; i < nums.length; i++) {
      total+=nums[i];
   }
   return total;
}

A complex object is treated differently than a scalar argument in a function. In the solution, an array is passed to the makeHello function, but isn’t returned from the function. However, the generated value added to the array in the function is still accessible to the calling program, because objects are passed into functions by reference.

Scalar arguments are passed by value, which means that a copy of the argument is made for processing in the function and changes to the argument in the function won’t be reflected back to the calling program. Objects, though, are passed by reference: any changes to them in the function are reflected in the calling program.

Example 3-2 demonstrates the differences between scalar and complex objects when used as function arguments. The function takes two parameters, a string literal and an array. Both are modified in the function, and the contents of both are printed after the function returns control to the calling program.

Example 3-2. Functional differences between scalar and array arguments
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Arguments passed by reference</title>

<style id="jsbin-css">
body {
  margin-top: 50px;
}
button {
        background-color: #3994b6;
        padding: 10px;
        -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
        border-radius: 5px;
        color: #FFF;
        text-align: center;
        text-decoration: none;
        font-size: 12px;
        font-weight: bold;
        width: auto;
  display: block;
  margin-bottom: 5px;
}
</style>
</head>
<body>
  <p>Arguments, separated by spaces:</p>
  <input type="text" id="args" />
  <button type="button" onclick="grabArgs()">
  Concatenate Strings</button>
  <div id="result"></div>
<script>
function concatenateString(strings, separator) {
  var result=strings.join(separator);

  // assign result to separator
  separator = result;

  // and array
  strings[strings.length]=result;
}

function grabArgs() {

  var sep = "*";
  var items = document.getElementById("args").value.split(" ");
  concatenateString(items,sep);

  document.getElementById("result").innerHTML =
    "<p>" + items + "<br />" + sep + "</p>";
}
</script>
</body>
</html>

The result shows that the array is impacted by the changes within the function, while the simple string argument (+separator+) does not reflect the change.

Creating a Dynamic Runtime Function

Problem

You need to create a function, but you won’t know its structure until runtime.

Solution

Use an anonymous function, created using the Function object constructor:

// two parameters and one function body string
var functionName = new Function (x, y, functionBody);
functionName(varA, varB); // two parameters are processed by function

Discussion

Functions created using the new Function object constructor are called anonymous functions because they’re not given a function name when they’re created. Instead, they’re assigned to a variable. You then use the variable as you would a function call.

Anonymous functions are parsed at runtime, which makes them inefficient for general purposes. However, they allow us to define both parameters and function body at runtime, which is handy if you’re not sure what the function body is going to be until runtime.

To demonstrate an anonymous function, I’m going to borrow an example from another of my books, Learning JavaScript (O’Reilly), replicated in Example 3-3. This JavaScript application prompts the web page reader to provide a function that takes two parameters, as well as the value of the parameters. The application then uses these values to create the function and invoke it.

Example 3-3. Using an anonymous function
<!DOCTYPE html>
<html>
<head>
<title>Anonymous Function</title>
<script>

window.onload=function() {

  var func = prompt("Enter function body:","");
  var x = prompt("Enter value for x:","");
  var y = prompt("Enter value for y:","");

  var newFun = new Function("x","y",func);
  var result = newFun(x,y);

}

</script>
</head>
<body>
</body>
</html>

When prompted for the function body, use something simple such:

alert(x + " " + y)

If you pass in Hello and World for the next two prompts, the result is an alert message with Hello World.

The function return value is assigned a variable, just in case the dynamic function body returns a value. If no value is returned, then the returned value is undefined.

Of course, using an anonymous function like this isn’t the safest thing to do, because you can’t control what the person enters as function body. And it’s not particularly useful either.

Anonymous functions can be useful when passing functions as arguments, or assigning them to object properties. However, in most of these cases a literal function is preferred over a function object, because function literals are parsed once, when the application is loaded.

See Also

Literal functions are covered in “Passing a Function As an Argument to Another Function”.

Passing a Function As an Argument to Another Function

Problem

You want to pass a function as an argument to another function.

Solution

For implementing a solution matching the following:

function otherFunction(x,y,z) {
  x(y,z);
}

use a literal function:

var param = function(arg1, arg2) { alert(arg1 + " " + arg2); };
otherFunction(param, "Hello", "World");

or a function expression as a function argument:

otherFunction(function(arg1,arg2) { alert(arg1 + ' ' + arg2); }, "Hello","World");

Discussion

The function keyword is an operator as well as a statement, and can be used to create a function as an expression. Functions created this way are called function expressions, function literals, and anonymous functions, though anonymous functions is an overloaded term since it applies to functions as objects, as well as functions as expressions.

A function name can be provided with literal functions, but it’s only accessible within the function:

var param = function inner() { return typeof inner; }
console.log(param()); // prints out "function"

This functionality may not seem useful, but it’s essential if you want to implement recursion, callback functionality, pass a function to an event handler or timer, and a host of other necessary functionalities.

You can pass a function as an argument to another function as a named variable, or even directly within the argument list, as shown in the solution. Unlike functions constructed as objects, function literals are parsed when the page is loaded, rather than each time they’re accessed.