CS131 - Spring 2004

Lesson 12: Strings and Objects


Throughout the course, you have manipulated string values in the same way you manipulated numbers and booleans. In particular, you could read and write strings, assign string values to variables, and even concatenate strings using '+'. In this lesson, you will learn more about the JavaScript string type and its uses. In particular, you will see how strings are different from numbers and booleans in that they are objects, encapsulating both data attributes and operations on that data.


Strings as Objects

Probably the hottest topic in software development today is "objects". Popular languages such as Java and C++ extol "object-oriented programming", while object-based modeling tools are used throughout the software industry. Simply stated, a software object is a structure that models an object in the real world. An object therefore encompasses more than just data: it also encompasses the operations that can be performed on that data.

For example, consider a common real-world object: a doorbell. A doorbell is defined not only by what it is made of (e.g., a button, a bell, wires) but also by what it does. A doorbell, when pressed, rings to alert you that a person is at the door. HTML buttons may be seen as software objects that model such real-world buttons. An HTML button has certain attributes (e.g., a name, a label) plus operations associated with it (e.g., code to be executed when the button is clicked).

Within a programming language, common data types and their operations can be encapsulated into objects for easy use. An example of this is the JavaScript string type. In addition to its value as a sequence of characters enclosed in quotes, a string has numerous attributes and predefined operations associated with it. For example, each string variable has a length attribute that keeps track of the number of characters in the string. When you assign a variable a string value, say   str = "foo",   the length attribute for that string is automatically assigned as well (in this case, the length is 3). Since an attribute belongs to a particular object, it is accessed by first specifying the object, then a period (which signifies ownership), and then the attribute. For example, the length of the string str can be accessed as   str.length.

In addition to attributes, strings have numerous operations predefined for them in the form of functions. For example, the toUpperCase function can be applied to a string, returning a copy of that string with all lower-case letters made upper-case. Likewise, the toLowerCase function returns the lower-case version of the string. As was the case with attributes, the functions associated with an object are applied to that particular object by specifying the object, then a period, then the function call. For example:

var str, upStr, downStr; str = "Hi 2 You"; upStr = str.toUpperCase(); // upStr = "HI 2 YOU" downStr = str.toLowerCase(); // downStr = "hi 2 you" The following HTML document demonstrates the use of the length attribute and the toUpperCase function. The page contains a text box into which the user can enter a string. The ONCHANGE attribute of the text box specifies that whenever the contents are changed, the length of the new string value is determined (by accessing the length attribute of the string) and displayed in a second text box. In addition, there is a button labeled "Click for UpperCase". When this button is clicked, the contents of the text box are made upper-case using the toUpperCase function.

<HTML> <!-- string.html 8/21/2000 --> <!-------------------------------------------------------> <HEAD> <TITLE> String Fun </TITLE> <SCRIPT LANGUAGE="JavaScript"> function getLength(theStr) // Assumes: theStr is a string value. // Returns: the length of the string. { var length; length = theStr.length; return length; } function toAllCaps(theStr) // Assumes: theStr is a string value. // Returns: the string in all uppercase. { var allCaps; allCaps = theStr.toUpperCase(); return allCaps; } </SCRIPT> </HEAD> <BODY> <CENTER> <H2>String Fun</H2> <FORM NAME="StringForm"> Enter a string: <INPUT TYPE="text" NAME="str" SIZE=20 ONCHANGE="document.StringForm.strLength.value = getLength(document.StringForm.str.value);"> &nbsp; &nbsp; has length <INPUT TYPE="text" NAME="strLength" SIZE=3 VALUE=0 ONFOCUS="blur();"> <BR><BR> <INPUT TYPE="button" VALUE="Click for UpperCase" ONCLICK="document.StringForm.str.value = toAllCaps(document.StringForm.str.value);"> </FORM> </CENTER> </BODY> </HTML>


EXERCISE 12.1:    Cut-and-paste the string.html text into a new HTML document and load this page to verify that it behaves as described.

Add an additional button labeled "Click for LowerCase". When this button is clicked, the contents of the text box should be made lower-case (using the toLowerCase string function).

Many of the exercises in this lesson ask you to add to the functionality of your string.html page. Thus, you will not need to include code in your answers for each individual exercise. Instead you will be asked to include your complete string.html page when it is complete. However, be sure to answer any questions that are asked in the individual exercises.




Accessing characters in a string

In addition to determining the length and changing the case of a string, it is possible to access individual characters in a string using the charAt function. The charAt function takes a non-negative integer as input, specifying an index, and returns the character stored at that particular index of the string. For example, the call str.charAt(0) will return the character at index 0 (i.e., the first character in str), str.charAt(1) will return the character at index 1 (i.e., the second character in str), and so on. Note that strings are indexed starting at 0, so the last character in a string str would actually be at index (str.length - 1).

Using the length attribute and charAt function, it is possible to traverse a string and act upon each character in that string. For example, the JavaScript code below constructs a copy of the string str using a for loop.

str = "abcd"; copy = ""; for (i = 0; i < str.length; i = i + 1) { copy = copy + str.charAt(i); } Initially, str is the string "abcd" and copy is the empty string. During each pass through the for loop, a character from str is accessed and concatenated onto the end of copy, yielding "a", then "ab", then "abc", and finally "abcd". More formally: i str.charAt(i) copy ----------------------------------------------- before loop - - "" end of 1st pass 0 "a" "a" end of 2nd pass 1 "b" "ab" end of 3rd pass 2 "c" "abc" end of 4th pass 3 "d" "abcd"


EXERCISE 12.2:    Consider a variation of the above code in which the statement in the body of the for loop is slightly modified: str = "abcd"; copy = ""; for (i = 0; i < str.length; i = i + 1) { copy = str.charAt(i) + copy; }

Using the above example as a guide, trace the behavior of this modified code in a table like the one shown below:

i str.charAt(i) copy ----------------------------------------------- before loop - - "" end of 1st pass end of 2nd pass end of 3rd pass end of 4th pass



EXERCISE 12.3:    Define a function named reverse that takes a string as input and returns a copy of that string in reverse order. For example, the function call reverse("abcd") should return the string "dcba". Add this function definition to the HEAD of your string.html page and add an additional button in the BODY that enables the user to reverse the contents of the text box.




EXERCISE 12.4:    Try to predict the behavior of the following function: function strip(str) // Assumes: str is a string // Returns: ??? { var i, nosp; nosp = ""; for(i = 0; i < str.length; i = i + 1) { if (str.charAt(i) != " ") { nosp = nosp + str.charAt(i); } } return nosp; }

In particular, what would the following function calls return?

strip("a b c d") strip(" foo ") strip("banana")

Add the definition of the strip function to your string.html page and add an additional button to enable the user to strip the contents of the text box.



Accessing substrings in a string

The charAt string function allows for accessing a single character within a string. Similarly, the substring function allows for accessing a sequence of characters, i.e., a substring, within a string. The substring function takes two non-negative integers as inputs, specifying the starting (inclusive) and ending (exclusive) indices for the substring. For example, the call str.substring(0, 2) will return the substring consisting of the first two characters in the string str (indices 0 and 1), while str.substring(1, 4) will return the substring consisting of three characters (indices 1, 2, and 3).


EXERCISE 12.5:    Assuming word = "smarmy", try to predict the values returned by each of the following calls to substring: word.substring(0, 5) word.substring(4, 5) word.substring(1, word.length) word.substring(0, word.length-1)



EXERCISE 12.6:    Define a function named capitalize that takes a string as input and returns a copy of that string capitalized. That is, the first letter of the string should be made upper-case, and all other letters should be lower-case. For example, the function call capitalize("fooBar") should return the string "Foobar". Add this function definition to the HEAD of your string.html page and add an additional button in the BODY that enables the user to capitalize the contents of the text box.

Hint: to capitalize a string, you must first break it into two pieces, the first character and the remaining substring. If you then make the first character upper-case and the remaining substring lower-case, you can concatenate them back together to obtain the desired capitalized word. For example,

capitalize("fooBar") = "F" + "oobar" = "Foobar"



EXERCISE 12.7:    Define a function named rotateRight that takes a string as input and returns a copy of that string with the characters rotated one position to the right. That is, the first character is shifted from index 0 to index 1, the second character is shifted from index 1 to index 2, and so on, with the last character shifted back around to index 0. For example, the function call rotateRight("abcde") should return the string "eabcd". Add this function definition to the HEAD of your string.html page and add an additional button in the BODY that enables the user to rotate the contents of the text box to the right.

Hint: to rotate a string to the right, you must first break it into two pieces the substring starting at the beginning and continuing up to the last character and the last character in the string. If you then concatenate them in reverse order, with the character first and substring second, the result is the desired string. For example,

rotateRight("abcde") = "e" + "abcd" = "eabcd"



EXERCISE 12.8:    Define a function named rotateLeft that similarly rotates the characters in a string, only this time to the left. For example, the function call rotateLeft("abcde") should return the string "bcdea". Add this function definition to the HEAD of your string.html page and add an additional button in the BODY that enables the user to rotate the contents of the text box to the left.



Searching a string

The final string function that we will consider is the indexOf function, which allows for searching a string for a given substring. The indexOf function takes a string as input, and returns the index where that string first occurs in the larger string. For example, if   str = "banana",   then the call str.indexOf("ana") will return 1, since the substring "ana" first occurs in str starting at index 1. If the desired substring does not occur in the string, the indexOf function returns -1.

The indexOf function is used in the following function to strip all non-letters out of a string. It is similar to the strip function from EXERCISE 12.4, except that it uses indexOf in the if statement condition. For each character in the string str, the indexOf function is called to see if that character can be found in the alphabet string. If so (i.e., if the function returns a value other than -1), then that letter is concatenated onto the newStr variable. If not (i.e., the function returns -1), then the non-letter is ignored.

function stripNonLetters(str) // Given : str is a string // Returns: a copy of str with all non-letters removed { var alphabet, newStr, i; alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; str = str.toUpperCase(); newStr = ""; for (i = 0; i < str.length; i++) { if (alphabet.indexOf(str.charAt(i)) != -1) { newStr = newStr + str.charAt(i); } } return newStr; }


EXERCISE 12.9:    Cut-and-paste this function definition into the HEAD of your string.html page and add an additional button in the BODY that enables the user to strip non-letters from the contents of the text box. Verify that this function behaves as desired.

Note: since stripping non-letters from the word in the text box can change its length, you should be sure to update the contents of the strLength box as well when the button is clicked.

At this point you are finished working with string.html. So include the full HTML for this page as your solution for exercise 12.9.



EXERCISE 12.10:    A palindrome is a word or phrase that reads the same backwards as forwards. For example, the word "madam" is a palindrome. With what you have learned in this lesson, you could easily create a Web page that tested to see whether a word was a palindrome. For example, you could have the user enter text into a box, use your reverse function to reverse the contents of the box, and then test to see whether the reversed text was identical to the original text. While this implementation would be simple, it would also be inefficient. Consider the word "madame", for example. Clearly, this word is not a palindrome since it doesn't even begin and end with the same letter. However, the above approach would still go through the work of reversing the word before checking to see if the letters match. Consider the more efficient, but slightly more complex, approach as implemented in the function below: function isPalindrome(word) // Given : word is a string // Returns: true if word is a palindrome, else false { var i; for (i = 0; i < word.length; i++) { if (word.charAt(i) != word.charAt(word.length-1-i)) { return false; } } return true; } Trace the execution of the call isPalindrome("repeater"). That is, for each pass through the loop, specify which characters are being compared. What value is returned by the function call? i word.length-1-i word.charAt(i) word.charAt(word.length-1-i) ------------------------------------------------------------------------------------- 1st pass 0 7 "r" "r" 2nd pass . . . VALUE RETURNED:

When the above isPalindrome function is called with a word that is not a palindrome, it terminates as soon as non-matching characters are found. If the word is a palindrome, however, the function does twice as many character comparisons as is necessary. Explain why this is the case. What modification would you have to make to the for loop so that it performs only as many comparisons as are necessary?



EXERCISE 12.11:    Using your improved version of the isPalindrome function, design and implement a new Web page named pal.html for testing whether a word or phrase is a palindrome. Your page should be case-insensitive, and should ignore any non-letters when testing the text. (Hint: make use of the toUpperCase and stripNonLetters function from earlier exercises.) Thus, the following words and phrases would all be considered palindromes: Bob Madam, I'm Adam. Able was I ere I saw Elba. Golf? No sir -- prefer prison flog! (A palindromic baseball poem.) A lob; a rap as bat stabs; a parabola! Doc, note: I dissent. A fast never prevents a fatness. I diet on cod. Loofahs in a violin! In a a gap in my hymn! I, Paganini - lo, I vanish - a fool! A man; a plan; a canal: Panama! (Contributed by Theordore Roosevelt.) A man, a plan, a canoe, pasta, heros, rajahs, coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!



Lesson Summary


Solutions to odd numbered exercises.