reptile7's JavaScript blog
Wednesday, June 28, 2006
 
Array Miscellaneous
Blog Entry #43

I was at my local Starbucks, sipping a venti dark roast as is my custom, thinking about JavaScript arrays, and I wondered, "Just what is the scope of these things? What all can we organize as an array?" In the Primer #26 Script, we created an array of text strings. Recalling Netscape's array definition - "an ordered set of values that you refer to with a name and an index" - it should be clear that we can create arrays of other types of values; for example, we can create an array of numbers:

squares = new Array(1,4,9,16,25,36,49,64,81,100);

Or an array of Boolean values:

tralse = new Array(false,true,false,true,true,false);

Or even an array of the null and undefined primitive values:

dontknow = new Array(null,undefined,undefined,null);

What else? Given the connection between arrays and variables, it occurred to me that anything that can be variabilized, directly or indirectly - properties, objects, methods, whatever - should be arrayable.

Property arrays

"Properties and arrays in JavaScript are intimately related; in fact, they are different interfaces to the same data structure," Netscape notes in the "Objects and Properties" section of Chapter 7 ("Working with Objects") of its JavaScript 1.5 Core Guide. In illustration, consider the image element coded below:

<img name="myImage" width="150" height="150" border="0" src="some_image.gif">

For this image, we can easily create an array of properties that is manipulable by the methods of the Array( ) object:

<script type="text/javascript">
imgprop = new Array( );
imgprop[0] = document.myImage.border;
imgprop[1] = document.myImage.height;
imgprop[2] = document.myImage.name;
imgprop[3] = document.myImage.src;
imgprop[4] = document.myImage.width;
</script>

Method example
We can reverse the order of the imgprop elements with the reverse( ) method:

imgprop.reverse( );
window.alert(imgprop[4]);
// the alert( ) box displays 0, as document.myImage.border is now imgprop[4]

Object arrays

The following objects are constructible (i.e., can be created with a constructor statement) and are thus arrayable:
Built-in JavaScript objects*: Array, Boolean, Date, Function, Number, Object, RegExp, String
DOM host objects: Image**, Option
(*The built-in Math object is not constructible; an optimistic attempt to execute a mathtest = new Math( ) statement met with an error.)
(**We will discuss the new Image( ) constructor in Primer #27.)

For example, we can create an array of Date objects:

beatbirth = new Array( );
beatbirth[0] = new Date("July 7, 1940"); // octopus
beatbirth[1] = new Date("October 9, 1940"); // walrus
beatbirth[2] = new Date("June 18, 1942"); // obladi
beatbirth[3] = new Date("February 24, 1943"); // taxman

In applying the Array( ) object to host objects, we are not limited to the image and option objects. We learned in Blog Entry #26 that newly opened windows can be variabilized; we can thus create an array of windows - and by extension, an array of the documents they hold - as follows:

winx = new Array( );
winx[0] = window.open(URL0);
winx[1] = window.open(URL1);
winx[2] = window.open(URL2);
winx[3] = window;
// winx[3] references the opener window; winx[3] = window.opener would throw an error

Pushing the envelope a bit further, it would seem that any referenceable document element can be an Array( ) element. Consider a document containing the following three forms:

<form name="f00">
<input name="c00" value="Text box #1"><br>
<input name="c01" value="Text box #2"><br>
</form>
<form name="f01">
<input type="button" name="c02" value="Button #1"><br>
<input type="button" name="c03" value="Button #2"><br>
</form>
<form name="f02">
<select name="c04">
<option>Option #1</option>
<option>Option #2</option>
</select></form>

As you know, the f00, f01, and f02 forms compose the document.forms[i] array. However, we can certainly create a separate array of these forms using the Array( ) object if for some reason we wanted to do that:

formx = new Array( );
formx[0] = document.f00;
formx[1] = document.f01;
formx[2] = document.f02;

We can similarly create an Array( ) of the f00, f01, and f02 form controls; it is left to the reader to write out the code therefor.

Method arrays

Some methods output values and thus can be arrayed, for example:

prox = new Array( );
prox[0] = window.prompt("Do you prefer vanilla or chocolate ice cream?");
prox[1] = window.prompt("Do you prefer coffee or tea?");
prox[2] = window.prompt("Do you use a PC or a Mac?");
window.alert(prox.join("/"));
// the join( ) method of the Array( ) object is outlined here

Try it out:


However, method commands that don't return values are still indirectly arrayable via the built-in Function( ) object. In JavaScript, methods are in fact a subset of functions - "[a] method is a function associated with an object," Netscape declares in its "Defining Methods" documentation. The code below creates an array of specific window.alert( ), document.write( ), and text_object.focus( ) commands:

meth = new Array( );
meth[0] = new Function("window.alert('Hello, world!');");
meth[1] = new Function("document.write('Laissez les bons temps rouler!');");
meth[2] = new Function("document.f00.c00.focus( );");

Etc.

Associative arrays

Although array elements are indexed automatically with ordinal numbers, we can also index them with strings:

array_name = new Array( );
array_name["string0"] = value0;
array_name["string1"] = value1; // etc.

Such arrays are termed associative arrays "because each index element is also associated with a string value," quoting Netscape. We had a brief taste of associative arrays in Blog Entry #32 when we noted that an <img name="imagename"> element can be referenced with document.images["imagename"].

An associative array is put to creative use in the guitar chord script discussed in HTML Goodies' JavaScript Script Tips #56-59.

Two-dimensional arrays

In the previous post, I drew a parallel between arrays and mathematical matrices; making the connection more explicit is the fact that arrays can be two-dimensional. (And Netscape's sample code is clearly extensible to the construction of higher dimensional arrays.) I'm not inclined to talk about 2D arrays here and now but will note that a 2D array figures prominently in the "pop-up tables" script discussed in HTML Goodies' JavaScript Script Tips #92-93.

More Array( ) method demos

The "Arrays" section of HTML Goodies' recently posted "JavaScript Basics Part 5" article sports several Array( ) method demos that are worth checking over. (Make sure your browser is up-to-date! Specifically, if you're using a pre-5.5 version of Internet Explorer, then the push( )/splice( ) and pop( ) demos won't work for you.)

We'll continue "putting it all together" in the next post as we examine HTML Goodies' JavaScript Primers #27, in which we'll code a simple slide show. Who needs PowerPoint, anyway?

reptile7

Sunday, June 18, 2006
 
The Collective
Blog Entry #42

We soldier on to HTML Goodies' JavaScript Primers #26, "Putting it all together: Functions from a Function." The Primer #26 Script does indeed feature a function calling another function, as we'll see below; "[h]ere's something new!" Joe enthuses as he addresses the relevant code in his script deconstruction. Not quite, Dr. Burns - we actually saw this sort of thing back in the Primer #22 Script, whose guessnum( ) function calls a preceding rand( ) function when guessnum( )'s if condition is true.

The real 'star of the show' in Primer #26 is the built-in, not-part-of-the-DOM JavaScript Array object. We've heretofore discussed or at least given mention to five other built-in JavaScript objects: Date, Function, Math, Number, and String; consult Chapter 1 ("Objects, Methods, and Properties") of Netscape's JavaScript 1.5 Core Reference for a complete list of these objects. In a programming sense (or nonprogramming sense, for that matter), "array" is synonymous with "set" or "collection." In "The Concept" of Primer #26, Joe defines a JavaScript array constituting an Array object as "a variable that contains many values" - a fair-enough description as far as it goes, and similar to what JavaScript Kit says about arrays in its "Understanding and implementing arrays" tutorial - but I for my part prefer the definition that appears in Netscape's JavaScript 1.5 Core Guide: "An array is an ordered set of values that you refer to with a name and an index."

More abstractly, Netscape also says, "Properties and arrays in JavaScript are intimately related; in fact, they are different interfaces to the same data structure." Think of the document object, whose global properties - bgColor, fgColor, title, etc. - collectively compose an array of elements, each with its own value.

The built-in Array object is distinct from host object arrays/collections - e.g., window.frames[i], navigator.plugins[i], document.forms[i], etc. - whose use we introduced many moons ago (check the last third or so of Blog Entry #13, for example) and which Joe briefly discusses in the "Here's a little more on arrays:" section at the end of Primer #26. The Array object and host object arrays have in common a length property, but to the best of my knowledge, none of the methods or other properties of the Array object is applicable to host object arrays.

Disappointingly, there is no information on the Array object, its properties, or its methods on any of HTML Goodies' JavaScript References pages.

The Primer #26 Script

Let's move from theory to practice and check out the Primer #26 Script:

<html><head>
<script type="text/javascript">
states=new Array( );
states[0]="CT";
states[1]="PA";
states[2]="NJ";
states[3]="NY";
states[4]="RI";
num=0;
function pickstate( ) {
now=new Date( );
num=(now.getSeconds( ))%5; }
function whichstate( ) {
pickstate( );
guess="****";
while (states[num] != guess.toUpperCase( )) {
guess=prompt("What's my favorite state: CT, PA, NJ, NY, or RI?");
if (guess.toUpperCase( ) == states[num]) {
alert("That's my favorite state!"); }
else {alert("No, Try again") } } }
</script></head><body><form>
<input type="button" value="Guess" onClick="whichstate( );">
</form></body></html>

Borrowing features from both the Primer #22 Script and the Primer #23 Script, the Primer #26 Script codes a guessing game that asks the user to guess the scriptwriter's "favorite state," which is generated randomly, from a list of states; an appropriate alert( ) message indicates a correct or an incorrect guess. Focusing on the document head code, the Primer #26 Script has three main parts:
(1) The states array;
(2) The pickstate( ) function; and
(3) The whichstate( ) function.

The states array

Before the guessing game is started by clicking the "Guess [my favorite state!]" button, the states array is loaded into memory via the following code:

states=new Array( );
states[0]="CT";
states[1]="PA";
states[2]="NJ";
states[3]="NY";
states[4]="RI";

The first command line, states=new Array( ), declares with a constructor statement a new Array object - specifically and initially, an array of length=0 because Array( ) has no arguments - and assigns it to the identifier states.

The next five statements assign values to the first five elements of the states array. Recall Joe's definition of an array given earlier: "a variable that contains many values." The regular syntax for specifying those variable values is similar to that for host object arrays:

array_name[i] = "some value";
// i is an index number that starts counting at 0

Each value of states is a text string now associated with an index number that will double as a random number later in the script. As you might guess, these five values will be the "favorite state" choices in the guessing game.

There are two other syntaxes we can use to create the states array:

(1) A 'condensed' array syntax that declares and initializes the array via a single statement in which the array element values appear as comma-delimited Array( ) arguments:

states = new Array("CT","PA","NJ","NY","RI");

(2) An array literal syntax that simply lists the array element values as a sequence in square brackets:

states = ["CT","PA","NJ","NY","RI"];

From this form, it should be clear that an array is analogous mathematically to a matrix.

In both cases, the array element values are indexed as they are in the 'expanded' array syntax: states[0] is "CT", states[1] is "PA", etc. All three array syntaxes are nicely summarized at JavaScript Kit's Array object page.

The pickstate( ) function

The now=new Date( ); num=(now.getSeconds( ))%5; code generates a random number num whose value is 0, 1, 2, 3, or 4, these being the possible remainders for divisions using 5 as the divisor; the values of num will double as the index numbers of the states array elements later in the script.

You've probably noticed that num is declared globally after the states array is created. Is the num=0 statement necessary, given that this initial value of num will be overwritten by the pickstate( ) function? Not at all - leave it out.

The whichstate( ) function

Let's get the guessing game rolling. The user clicks the "Guess [my favorite state!]" button, triggering the whichstate( ) function.

The whichstate( ) function begins with a pickstate( ) function call - here's our 'function from a function,' per the primer title - which generates the random number num, as noted above. Perhaps you are thinking, "The pickstate( ) function really shouldn't be necessary - we should be able to put the random number code in the whichstate( ) function," and you would be correct. Moreover, the states array can be created inside the whichstate( ) function, if desired.

The meat of the whichstate( ) function is the following condition-controlled while loop:

while (states[num] != guess.toUpperCase( )) {
guess=prompt("What's my favorite state: CT, PA, NJ, NY, or RI?");
if (guess.toUpperCase( ) == states[num]) {
alert("That's my favorite state!"); }
else {alert("No, Try again") } }

The while condition compares states[num] and guess.toUpperCase( ) using the != not equal comparison operator. This is the first time in the HTML Goodies JavaScript Primers series that Joe uses the != operator and he provides no commentary thereon in the script deconstruction (we for our part first used the != operator in a similar context in Blog Entry #38). In this case, the while condition returns true, and the while loop body is executed repeatedly, as long as states[num] is not equal to guess.toUpperCase( ).

The left operand, states[num], returns a random value of the states array. The states[num] expression coordinates the values of num with the index numbers i of the states[i] array elements by plugging num into the square brackets that follow states[ ]; because num is random, then states[num] is also random.

The right operand applies to the variable guess the self-explanatory toUpperCase( ) method of the String object, which is not listed on the end-of-primer "Click Here For What You've Learned page but which does at least appear in the HTML Goodies JavaScript methods keyword reference; you'll recall that we made use of the corresponding toLowerCase( ) method in Blog Entry #37. Just prior to the while statement, guess is declared/initialized as a string of four asterisks, ensuring that the while condition returns true and that the while loop body is executed by the browser for at least a first iteration; we'll see below that we can lose the guess="****" statement if we recast the while loop as a do...while loop (both while and do...while loops were discussed in the previous post).

With its condition initially true, the while loop then pops up a prompt( ) box that asks the user to guess a state from the five states constituting the states array; the user types in a response, which is assigned to guess. At this point, you can sort out what happens: the while loop's if and else statements handle correct and incorrect guesses, respectively. For a correct guess, the if condition, guess.toUpperCase( ) == states[num], is true and the script displays a "That's my favorite state!" alert( ) message; the while condition is now false and the loop stops. For an incorrect guess, the script displays a "No, Try again" alert( ) message; the while condition is still true and the loop continues.

Putting it all together, we can write the document head code of the Primer #26 Script much more compactly as follows:

<script type="text/javascript">
states = ["CT","PA","NJ","NY","RI"];
function whichstate( ) {
now=new Date( ); num=now.getSeconds( )%5;
do {
guess=prompt("What's my favorite state: CT, PA, NJ, NY, or RI?");
if (guess.toUpperCase( ) == states[num]) alert("That's my favorite state!");
else alert("No, Try again"); }
while (states[num] != guess.toUpperCase( )) }
</script>

The Primer #26 Assignment

For the Primer #26 Assignment, Joe asks the reader to code a 'random link generator,' i.e., a script that takes the user to a random Web page upon clicking a button. The idea here is to create an array of URL string values - array_name[i]="http://www.some_web_page.com" - that for the assignment is to be coordinated with a random number num via a top.location.href=urls[num] statement.

In his assignment answer, Joe somewhat annoyingly uses newurls and not urls for his array identifier, but what I really want to comment on is his use of the top property of the window object. In the "Referencing a Window: window vs. self vs. parent vs. top" section of Blog Entry #18, we noted, "Outside of a frames situation, 'window', 'self', 'parent', and 'top' all refer to the current active window and can be used interchangeably" and that "[i]n most cases, it is not necessary to reference 'window' either as an object or as a property." Consequently, the top reference in the answer script serves no real purpose and we can simply use location.href=newurls[num] (or even just location=newurls[num], short for window.location=newurls[num]) to set up a random link.

Before we move on to Primer #27, I've got a bit more to say about arrays and the Array object, and I'll do that in the next entry.

reptile7

Thursday, June 08, 2006
 
Taking JavaScript Mountain, By Strategy
Blog Entry #41

We continue today our introductory treatment of JavaScript looping techniques. Having discussed for loops in our last episode, we turn our attention in this entry to while loops, the focus of HTML Goodies' JavaScript Primers #25. In "The Concept[s]" of Primers #24 and #25, Joe informs us, "In general, you use For loops when you know how many times you want to perform a loop. Use While loops when you are not sure how many times you want to perform a loop." Somewhat more formally, using Wikipedia's 'taxonomy,' we can classify a for loop as a count-controlled loop and a while loop as a condition-controlled loop. Joe then proceeds to use a while loop in the Primer #25 Script in a count-controlled manner; in fairness to Joe, however, Netscape, DevGuru, and JavaScript Kit all do the same with their respective while loop examples. Ultimately, there is indeed much more similarity than difference between for loops and while loops.

Like for loops, while loops are not unique to JavaScript, but also appear in other programming languages. The syntax of a JavaScript while loop is given below:

while (condition) {
// as long as the condition is true, execute the following code repeatedly
command_statement_A;
command_statement_B; etc. }
// the braces are optional if there's just one command statement

After the while keyword is a set of parentheses containing a Boolean condition that is evaluated at the beginning of each iteration of the loop; if the condition is true, then the subsequent loop code in braces executes repeatedly, as noted above, but if and when the condition becomes false, then the loop stops.

Unlike the parenthesized expressions of a for loop, the while condition is not optional. Consider the following for loop, which faithfully reproduces the effect of the Primer #24 Script:

<script type="text/javascript">
var i = 1;
for ( ; ; ) {
if (i == 6) break
document.write(i + "<br>");
i=i+1; }
</script>

If you were to replace for ( ; ; ) with while ( ), then you'd get a syntax error.

Let's turn now to the Primer #25 Script:

<html><head></head><body>
<script type="text/javascript">
loops=3;
num=1;
while (num <= loops) {
document.write("Happy ");
num=num+1; }
document.write("Birthday");
</script></body></html>

The script's while statement, as noted previously, is a count-controlled loop. We've got an initial-expression, num=1, a condition, num <= loops, and an increment-expression, num=num+1, so we can wave our magic wand and - Poof! - easily turn the while loop into a for loop, if desired:

loops=3;
for (num=1; num <= loops; num=num+1) {
document.write("Happy "); }
document.write("Birthday");

For a count-controlled while loop, the placement of expressions is important: the initial-expression is necessarily placed before the while statement, whereas the increment-expression is necessarily placed inside the while loop {body}. Other than that, I don't have anything to add to Joe's script deconstruction.

The do...while statement

As detailed above, a while statement (1) initially evaluates its condition and (2) then executes or doesn't execute its command statements. JavaScript also has a related do...while looping statement that reverses this order of operations, ensuring that the loop {body} is executed at least once, according to the following syntax:

do {
command_statement_A;
command_statement_B; etc. }
while (condition)

The browser executes the do commands in the first (and possibly last) iteration of the loop, and then the while condition is evaluated; subsequently, the do commands are re-executed, and repeatedly so, if and as long as the while condition is true, but are not re-executed if the while condition is false.

In illustration, consider the while loop that appears in the random-number-guessing-game guessnum3( ) function in the "Other code possibilities" section of Blog Entry #38:

function guessnum3( ) {
now=new Date( ); num=(now.getSeconds( ))%10; num=num+1;
guess=prompt("Your guess?");
if (guess == num) {alert("YOU GUESSED IT!!");}
while (guess != num) {
alert("No. Try again.");
guess=prompt("Your guess?");
if (guess == num) {alert("YOU GUESSED IT!!");} } }

At last, a bona fide condition-controlled while loop! And with respect to the present discussion, a while loop that is more efficiently formulated as a do...while loop, which in this case allows the elimination of duplicate prompt( ) commands and if statements:

function guessnum3a( ) {
now=new Date( ); num=(now.getSeconds( ))%10; num=num+1;
do {
guess=prompt("Your guess?");
if (guess == num) alert("YOU GUESSED IT!!");
else alert("No. Try again."); }
while (guess != num) }

Try it out:


The Primer #25 Assignment

In the Primer #25 Assignment, Joe asks the reader to add some user interactivity to the Primer #25 Script, specifically, to modify the script so that, in lieu of presetting a three-iteration loop with the loops=3 statement, the user is able to customize the number of while iterations, and thus the number of "Happy "s written to the page, via a number entered into a prompt( ) box.

We noted in Blog Entry #37 that prompt( ) outputs are strings, and Joe's assignment answer duly makes use of the eval( ) function to convert tellme, the identifier to which his prompt("How many Happy's do you want before the word Birthday?") command is assigned, to a number. For the first time (this being the third time we've seen the eval( ) function), Joe comments in the assignment on the purpose of eval( ): "Remember to use Eval( ) to change the [prompt( )] response from text to a number." And eval( ) finally makes an appearance on the end-of-primer "Click Here For What You've Learned" page (although its definition there is seriously incomplete). Better late than never!

However, as in the Primer #22 Script, the use of the eval( ) function in the Primer #25 Assignment answer is in fact unnecessary. With the exception of the === (strict equal) and !== (strict not equal) operators, all of JavaScript's comparison operators compare their operands without regard to data type, with the browser reconciling different data types as necessary. We can thus rewrite the assignment answer script as:

<script type="text/javascript">
tellme=prompt("How many 'Happy's do you want before the word Birthday?");
num=1;
while (num <= tellme) {
document.write("Happy ");
num=num+1; }
document.write("Birthday");
</script>

Again, tellme will be converted to a number when the while condition is evaluated, obviating the need for a loops=eval(tellme) statement.

We'll see new things and old in Primer #26, which we'll cover in the next post; most importantly, we'll introduce the built-in JavaScript Array object, and we'll also look at another guessing-game script that features if...else and while statements.

reptile7


Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)