Chapter 5. Working with Arrays and Loops
5.0. 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. On the positive side, though, array literals
can replace the need for temporary variables, especially when sending
values to a function.
A new Array
object is created
using the new
operator, as
follows:
var arrObject = new Array();
You can also create a new array that has some values:
var arrObject = new Array("val1","val2");
You can create an array literal by using square brackets to hold the array values. For instance, you can define an array literal and assign it to a variable:
var arrLiteral = ["val1","val2","val3"];
You can also create, and use, a literal array in a function or method call:
someFunction("param1", ["val1","val2"]);
Note, though, that when you pass a variable containing an array
literal to a function, it is passed by reference—the same as passing a
variable holding an Array
object.
Changes to the variable in the function are reflected outside of the
function:
function chgArray(arr) { arr[0] = "surprise!"; } var newArray = new Array("val1", "val2"); var newLiteral = ["val1","val2"]; chgArray(newArray); chgArray(newLiteral); alert(newArray); // prints surprise!,val2 alert(newLiteral); // prints surprise!,val2
An array, whether literal or object, can hold values of different data types:
var arrObject = new Array("val1", 34, true); // string, number, boolean var arrLiteral = [arrObject, "val2", 18, false); // object, string, number, boolean
You can print out an array; the JavaScript engine will automatically convert the array into a string representation:
alert(arrLiteral); // prints out val1,34,true,val2,18,false
In this example, the JavaScript engine makes the array-to-string conversion for both the array literal and the array object contained as an element within the array literal.
Array elements can be accessed directly, using square brackets containing their index (position in the array). In addition, array elements can be set using the same index, which automatically creates the array element if it doesn’t exist:
var arrObject = new Array(); arrObject[0] = "cat"; // array now has one element alert(arrObject[0]); // prints cat
Arrays in JavaScript are zero-based, which means the first element index is zero, and the last element is at the array length, minus 1:
var farmAnimals = new Array("cat","dog","horse","pig"); alert(farmAnimals[0]); // print cat alert(farmAnimals[3]); // print pig
Not all array elements have to be defined when created. For instance, if you create an array literal, you can use commas to delimit array elements that don’t yet exist:
var arrLiteral = ["val1",,"val3"];
In this code, the second array element is currently undefined
. You can’t use
the empty comma, though, to add an undefined array element to the end of
the array: JavaScript will just ignore it.
To create an array of several undefined elements, you can provide an array length when creating an array:
var largeCollection = new Array(100); // a new array with 100 undefined elements
One you’ve created an array, using Array
object or literal notation, you can access
the array elements in a loop, or use any number of array methods.
5.1. Looping Through 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] + " "; } alert(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) { alert(numArray[i++]); }
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.
5.2. Creating a Multidimensional Array
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, use the code snippet in Example 5-1.
// 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']; alert(multiArray); // printed out in first index order alert(multiArray[2]); // prints out subarray alert(multiArray[2][2][0]); // individual item
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 5-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 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 printed out via an alert window, 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 5-1 shows a simple five-column, three-row table containing a set of numbers.
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.
5.3. Creating a 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 strings 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 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
5.4. Sorting an Array
Solution
Use the Array
object’s sort
method:
var fruitArray = ['strawberry','apple','orange','banana','lime']; alert(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]; alert(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]; alert(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 number conversion is automatic:
var numberArray=["34","4","5"]; alert(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
5.5. Store and Access Values in Order
Problem
You want to store values in such a way that you can access the values in the order in which they were stored.
Solution
To store and access values in the order in which they’re received,
create a FIFO (first-in, first-out) queue. Use the JavaScript
Array
object push
method to add items to the queue, and shift
to retrieve:
// create new array var queue = new Array(); // push on three entries queue.push('first'); queue.push('second'); queue.push('third'); // shift two entries alert(queue.shift()); // returns first alert(queue.shift()); // returns second alert(queue); // returns third
Discussion
A queue is an array of elements that are added one at a time, and retrieved in a first-in, first-out order (FIFO). Think of a line at the bank: people go to the end when they arrive at the bank, and tellers help those in the front of the line, who have been there the longest.
You could emulate this behavior using counter variables to hold
the index of the last item added (the end), and the index of the last
one retrieved (from the front), but luckily, the JavaScript Array
object provides methods that handle this
information for us, and also keep the array clean in the process.
The Array push
method creates a
new array element and adds it to the end of the array:
queue.push('first');
The array element count increments with each pushed element.
The Array shift
method extracts
the array element from the front of the array, removing it from the
array, and returning the element:
var elem = queue.shift();
The array element count decreases by one with each shifted
element, as shift
also modifies the
array in addition to returning the item.
5.6. Store and Access Values in Reverse Order
Problem
You want to store values in such a way that you can access the values in reverse order: access the most recently stored value first, then a LIFO (last-in, first-out) stack.
Solution
To access stored values in reverse order (last item added is
accessed first), create a LIFO (last-in, first-out) stack. Use the
JavaScript Array
object’s push
method to add items to the stack, and the
pop
method to
retrieve:
// create new array var queue = new Array(); // push on three entries queue.push('first'); queue.push('second'); queue.push('third'); // pop two entries alert(queue.pop()); // returns third alert(queue.pop()); // returns second alert(queue); // returns first
Discussion
A stack is an array of elements, with each new element added to the top of the stack, and retrieved in a last-in, first-out (LIFO) order. Think of a stack of dishes: you add plates to the top as they’re washed, and retrieve them from the top when needed. You could use a variable holding an integer that tracks the end of the array after each addition and retrieval, but JavaScript provides the functionality we need.
The Array push
method creates a new array element and adds it to the end
of the array:
queue.push('first');
The array element count increments with each pushed element.
The Array pop
method extracts
the array element from the end of the array, removing it from the array,
and returning the element:
var elem = queue.pop();
The array element count decreases by one with each popped element,
as pop
modifies the array.
5.7. 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 alert(newArray); // prints out octopus,four,five,six // modify new newArray[1][1] = "kitten"; // print out old alert(origArray); // prints out one,two,octopus,four,five,kitten,seven,eight
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. Be aware, though,
that IE8 doesn’t support slice
.
5.8. Searching Through an Array
Solution
Use the new (ECMAScript 5) Array
object methods indexOf
and lastIndexOf
:
var animals = new Array("dog","cat","seal","elephant","walrus","lion"); alert(animals.indexOf("elephant")); // prints 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"); alert(animals.indexOf("cat")); // prints 1 alert(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"); alert(animals.indexOf("cat",2)); // prints 5 alert(animals.lastIndexOf("cat",4)); // prints 1
Currently, all of the book’s target browsers support indexOf
and lastIndexOf
, except for IE8.
See Also
As mentioned, not all browsers support indexof
and lastIndexOf
. A cross-browser method to
implement like functionality in these browsers is given in the Mozilla
documentation, at https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/indexOf.
Since IE8 doesn’t support indexOf
,
here’s the Mozilla workaround for the function:
if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length >>> 0; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in this && this[from] === elt) return from; } return -1; }; }
5.9. Flatten a Multidimensional Array
Solution
Use the Array
object concat
method to merge the array dimensions into a single
dimensional array:
var origArray = new Array(); origArray[0] = new Array("one","two"); origArray[1] = new Array("three","four"); origArray[2] = new Array("five","six"); origArray[3] = new Array("seven","eight"); // flatten array var newArray = origArray[0].concat(origArray[1],origArray[2],origArray[3]); alert(newArray[5]); // prints six
Discussion
The Array
object concat
method takes one or more arrays, and
appends the array elements on to the end of the contents of the parent
array on which the method was called. The merged array is then returned
as a new array.
One use for this type of functionality is to return a single dimensional array made up of elements from a multidimensional array, as shown in the solution.
5.10. Search and Remove or Replace Array Elements
Problem
You want to find occurrences of a given value in an array, and either remove the element or replace with another value.
Solution
Use the Array
methods indexOf
and splice
to find and
remove/replace array elements:
var animals = new Array("dog","cat","seal","walrus","lion", "cat"); // remove the element from array animals.splice(animals.indexOf("walrus"),1); // dog,cat,seal,lion,cat // splice in new element animals.splice(animals.lastIndexOf("cat"),1,"monkey"); // dog,cat,seal,lion,monkey
Discussion
The splice
method takes three
parameters. The first parameter is required; it’s the index where the
splicing is to take place. The other two parameters are optional: the
number of elements to remove, and a substitute. If the index is
negative, the elements will be spliced from the end, not from the
beginning of the array:
var animals = new Array("cat","walrus","lion", "cat"); // splice in new element animals.splice(-1,1,"monkey"); // cat,walrus,lion,monkey
If the number of elements to splice is not provided, all elements from the index to the end will be removed:
var animals = new Array("cat","walrus","lion", "cat"); // remove all elements after second animals.splice(2); // cat,walrus
The last parameter, the replaced value, can be a set of replacement values, separated by commas:
var animals = new Array("cat","walrus","lion", "cat"); // replace second element with two animals.splice(2,1,"zebra","elephant"); // cat,walrus,zebra,elephant,cat
Removing or replacing one element is handy, but being able to
remove or replace all instances of a particular element is even handier.
In Example 5-2, an array
is created with several elements, including multiple instances of a
specific value. The splice
method is
then used in a loop to replace all of the elements with this one value
with elements with a new value. The splice
method is used again, in a separate
loop, to remove the newly spliced elements.
<!DOCTYPE html> <head> <title>Looping and Splicing</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" > <script> var charSets = new Array("ab","bb","cd","ab","cc","ab","dd","ab"); // replace element while (charSets.indexOf("ab") != -1) { charSets.splice(charSets.indexOf("ab"),1,"**"); } alert(charSets); // **,bb,cd,**,cc,dd,** // delete new element while(charSets.indexOf("**") != -1) { charSets.splice(charSets.indexOf("**"),1); } alert(charSets); // bb,cd,cc,dd </script> </head> <body> </body>
The example works with all of this book’s target browsers except
for IE8, which doesn’t currently support either indexOf
or splice
.
See Also
See Recipe 5.8 for a workaround
for indexOf
.
5.11. Applying a Function Against Each Array Element
Problem
You want to use a function to check an array value, and replace it if it matches a given criterion.
Solution
Use the new ECMAScript 5 Array
object forEach
to attach a callback function to each
array element:
var charSets = new Array("ab","bb","cd","ab","cc","ab","dd","ab"); function replaceElement(element,index,array) { if (element == "ab") array[index] = "**"; } // apply function to each array element charSets.forEach(replaceElement); alert(charSets); // prints **,bb,cd,**,cc,**,dd,**
Discussion
In the last section, we used a while
loop to
traverse an array to find and replace a value, but how much more helpful
is it to use the forEach
method?
The forEach
method takes one
parameter, the function. The function itself has three parameters: the
array element, the index of the element, and the array. All three were
used in the function, replaceElement
.
First, the element’s value is tested to see if it matches a given
string, ab
. If matched, the array element’s
index is used to modify the array element’s value with the replacement
string, **
.
Note
Don’t return a value from the function passed to the forEach
method, as the value will be
discarded.
Chrome, Firefox, Opera, and Safari support forEach
, but IE8 does not.
See Also
The concept of callback functions is covered in more detail in Chapter 6.
Most modern browsers support forEach
. However, for those that don’t, you
can emulate the forEach
behavior
using the Array.prototype
property. Mozilla provides a description about how to emulate
forEach
at https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/forEach.
For completeness, I’ve duplicated the code below. To use, add the code
into a library function and make sure it’s processed before the forEach
method is needed:
if (!Array.prototype.forEach) { Array.prototype.forEach = function(fun /*, thisp*/) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in this) fun.call(thisp, this[i], i, this); } }; }
5.12. Applying a Function to Every Element in an Array and Returning a New Array
Problem
You want to convert an array of decimal numbers into a new array with their hexadecimal equivalents.
Solution
Use the Array
object map
method to create a new array consisting of elements from
the old array that have been modified via a callback function passed to
the map
method:
// function to convert decimal to hexadecimal function convertToHex(element,index,array) { return element.toString(16); } var decArray = new Array(23, 255, 122, 5, 16, 99); var hexArray = decArray.map(convertToHex); alert(hexArray); // 17,ff,a,5,10,63
Discussion
Like the forEach
method in
Recipe 5.11, the
ECMAScript 5 map
method allows us to
attach a callback function that is applied to each array element. Unlike
forEach
, though, the map
method results in a new array rather than
modifying the original array. Therefore, you won’t return a value when
using forEach
, but you must return a
value when using map
.
The function that’s passed to the map method has three parameters:
the current array element, the index for the array element, and the
array. The forEach
and map
methods are currently not supported by
IE8.
See Also
Most modern browsers support the Array
object map
method, but to ensure that the
functionality is present, you can use the Array.prototype
property to emulate the
method’s behavior. See how at the Mozilla
website.
For comprehensiveness, I’ve included the code for the workaround
below. To use, include the code in a library function that is processed
before the map
method is
needed:
if (!Array.prototype.map) { Array.prototype.map = function(fun /*, thisp*/) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); var res = new Array(len); var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in this) res[i] = fun.call(thisp, this[i], i, this); } return res; }; }
5.13. Creating a Filtered Array
Solution
Use the Array
object filter
method:
function removeChars(element,index,array) { return (element !== "**"); } var charSet = new Array("**","bb","cd","**","cc","**","dd","**"); var newArray = charSet.filter(removeChars); alert(newArray); // bb,cd,cc,dd
Discussion
The filter
method is another ECMAScript 5 addition, like forEach
and map
, covered in Recipes 5.11 and 5.12, respectively. Like them, the method
is a way of applying a callback function to every array element.
The function passed as parameter to the filter
method returns a Boolean value, true or
false, based on some test against the array elements. This returned
value determines if the array element is added to a new array: it is
added if the function returns true; otherwise, it is not added. In the
solution, the character string “**
” is filtered from
the original array when the new array is created.
The function has three parameters: the array element, the index
for the element, and the array, itself. The filter
method is not supported by IE8.
See Also
Support for filter
is fairly
broad, but to ensure access to the functionality, there is a way to
emulate the filter method using Array.prototype
. Mozilla details the approach
at https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter,
but I’ve copied the technique below. To use, include the function in
your code, and run the function before you need to access the filter
method:
if (!Array.prototype.filter) { Array.prototype.filter = function(fun /*, thisp*/) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); var res = new Array(); var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in this) { var val = this[i]; // in case fun mutates this if (fun.call(thisp, val, i, this)) res.push(val); } } return res; }; }
5.14. Validating Array Contents
Solution
Use the Array
object’s every
method to check that every element passes a given
criteria. For instance, the following code checks to ensure that every
element in the array is an alphanumeric character:
var elemSet = new Array("**",123,"aaa","abc","-",46,"AAA"); // testing function function textValue (element,index,array) { var textExp = /^[a-zA-Z]+$/; return textExp.test(element); } // run test alert(elemSet.every(textValue)); // false
Or use the Array
object’s
some
method to ensure that at least some of the elements pass
the criteria. As an example, the following code checks to ensure that at
least some of the array elements are alphanumeric strings:
var elemSet = new Array("**",123,"aaa","abc","-",46,"AAA"); // testing function function textValue (element,index,array) { var textExp = /^[a-zA-Z]+$/; return textExp.test(element); } // run test alert(elemSet.some(textValue)); // true
Discussion
The every
and some Array
object methods are the last of the
ECMAScript 5 Array
methods I’ll be
covering in this book. Unlike the Array
callback function methods I covered in
previous recipes in this chapter, every
and some
functions do not work against all array
elements; they only process as many array elements as necessary to
fulfill their functionality.
The solution demonstrates that the same callback function can be
used for both the every
and the
some Array
object methods. The
difference is that when using the every
method, as soon as the function returns
a false value, the processing is finished, and the method returns false.
The some
method, though, will
continue to test against every array element until the callback function
returns true. At that time, no other elements are validated, and the
method returns true. However, if the callback function tests against all
elements, and doesn’t return true at any point, the some
method returns false.
Which method to use depends on your needs. If all array elements
must meet certain criteria, then use every
; otherwise, use some
.
The callback function takes three parameters: the element, the
index for the element, and the array. Neither the some
or every
method are supported by IE8, but they
are supported by the other target browsers for this book.
See Also
Most modern browsers support every
and some
, but for those browsers that don’t (such
as most versions of Internet Explorer), you can emulate the behavior
using the Array.prototype
. Mozilla
covers how to do this at https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/some
and https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/every.
For comprehensiveness, I’ve also included the functionality below. To use, ensure that the script provided is processed before the methods are needed.
Here’s how to emulate some
:
if (!Array.prototype.some) { Array.prototype.some = function(fun /*, thisp*/) { var i = 0, len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); var thisp = arguments[1]; for (; i < len; i++) { if (i in this && fun.call(thisp, this[i], i, this)) return true; } return false; }; }
if (!Array.prototype.every) { Array.prototype.every = function(fun /*, thisp*/) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in this && !fun.call(thisp, this[i], i, this)) return false; } return true; }; }
5.15. Using an Associative Array to Store Form Element Names and Values
Solution
Use an associative array to store the elements, using the element identifiers as array index:
var elemArray = new Object(); // notice Object, no Array var elem = document.forms[0].elements[0]; elemArray[elem.id] = elem.value;
Iterate over the array using the for...in
statement:
for (var key in elemArray) { str+=key + "," + elemArray[key] + " "; }
Discussion
Most JavaScript arrays use a numeric index, such as the following:
arr[0] = value;
However, you can create an associative array in JavaScript, where the array index can be a string representing a keyword, mapping that string to a given value. In the solution, the array index is the identifier given the array element, and the actual array value is the form element value.
You can create an associative array, but
you’re not using the Array
object to
do so. Using the Array
object is
risky and actively discouraged—especially if you’re using one of the
built-in libraries that use the prototype
attribute
for extending objects, as people discovered when the popular Prototype.js library was first released several years
ago.
The earlier Prototype.js library made an assumption that most
array use in JavaScript is numeric index–based, like most of the earlier
examples in this chapter. The library extended the Array
object functionality via Array.prototype
, based on this assumption. But
extending Array
objects in this way
breaks the for...in
loop
functionality used to traverse an associative array created from an
Array
object.
It’s not that Prototype.js was “breaking” JavaScript. The for...in
loop was intended for one purpose:
iterating over an object’s properties, such as being able to loop
through the String
object’s
properties, or your own custom object properties.
When we use an Array
object to
create an associative array, what we’re really doing is adding new
properties to the array object, rather than adding new array elements.
You could actually create an associative array with a RegExp
or String
, as well as an Array
. The reason is that in JavaScript
objects are associative arrays. When you’re adding
a new array, element
:
obj[propName] = "somevalue";
what you’re really doing is adding a new object property:
obj.propName = "somevalue";
To further demonstrate how different the associative array is from
a numeric-based array, when you use an Array
to create an associative array, you
can’t access the array “elements” by index, and the length property
returns zero.
Instead of using an Array
object to create the associative array, use the JavaScript Object
directly. You get the exact same
functionality, but avoid the clashes with libraries that extend the base
Array
object using prototype
.
Example 5-3 shows a web page. Here, when the form is submitted, all of the form elements of type text are accessed and stored in an associative array. The element IDs are used as the array keyword, and the values assigned to the array elements. Once collected, the associative array is passed to another function that could be used to validate the values, but in this case just creates a string of keyword/value pairs, which is then displayed.
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Associative Array</title> <script type="text/javascript"> //<![CDATA[ // get the form element names and values function getVals() { var elems = document.getElementById("picker").elements; var elemArray = new Object(); for (var i = 0; i < elems.length; i++) { if (elems[i].type == "text") elemArray[elems[i].id] = elems[i].value; } checkVals(elemArray); return false; } // check values function checkVals(elemArray) { var str = ""; for (var key in elemArray) { str+=key + "," + elemArray[key] + " "; } document.getElementById("result").innerHTML = str; } //--><!]]> </script> </head> <body> <form id="picker" onsubmit="return getVals()"> <label>Value 1:</label> <input type="text" id="first" /><br /> <label>Value 2:</label> <input type="text" id="second" /><br /> <label>Value 3:</label> <input type="text" id="third" /><br /> <label>Value 4:</label> <input type="text" id="four" /><br /> <input type="submit" value="Validate" /> </form> <div id="result"></div> </body> </html>
In the example, notice that the array index is formed by the form
element’s id
. When the array is
traversed, the for
loop syntax used
is:
for (keyword in array)
This syntax accesses the array index, which is then assigned to the keyword variable that can be used to access the array value:
for (keyword in array) var a = array[keyword];
Figure 5-1 shows the example after values are typed into the form fields and the form is submitted.
This type of keyword/value pairing is commonly referred to as a hash map or hash table, though the JavaScript functionality isn’t a true hash map functionality. The reason why it isn’t a true hash map is that it doesn’t account for the fact that the same keyword could be used with multiple values, and the JavaScript version only accepts strings as keywords.
See Also
See Chapter 16 for more on the object
nature of JavaScript, and Recipe 16.3 for more information about
extending the built-in objects, such as Array
, using the prototype
property. For more on the risks
associated with associative arrays in JavaScript, read “JavaScript
‘Associative Arrays’ Considered Harmful”, by Andrew
Dupont.
Get JavaScript Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.