LEARN TO PROGRAM WITH SMALL BASIC: An Introduction to Programming with Games, Art, Science, and Math (2016)

16. STORING DATA WITH ASSOCIATIVE ARRAYS

On social websites like Facebook and LinkedIn, people enter information into text boxes, such as their names, relationship statuses, and even regular updates to their friends (like, “Oh noes!! I just stepped on a bug, and I think I got bug poisoning!”). Programs that need to search or filter this data may use associative arrays to store the various parts of the text.

In addition to the indexed arrays you used in Chapter 15, Small Basic supports other types of arrays that can simplify many programming tasks. In this chapter, you’ll start by learning about associative arrays. Then you’ll learn about the Array object, use it to create some fun applications, and even turn your computer into a poet!

Associative Arrays

In the previous chapter, you learned how to use an integer index to access an array’s elements. But in Small Basic, an array’s index can also be a string. Arrays indexed by strings are called associative arraysmaps, or dictionaries. In this book, we’ll call them associative arrays. Just like an indexed array, an associative array can store values of any type. You can use an associative array to create an association between a set of keys (string indices) and a set of values, which is called creating a map of key-value pairs.

The following code shows a simple example of an associative array in action. It’s a list of states keyed by their two-letter abbreviations:

state["CA"] = "California"
state["MI"] = "Michigan"
state["OH"] = "Ohio"
' ... and so on

To display the name of a state, you simply use its corresponding key and the proper syntax. For example, to display Michigan, you can write this statement:

TextWindow.WriteLine(state["MI"])

By writing the name of the array followed by the key enclosed in square brackets, you can access the corresponding item. An associative array works like a lookup table that maps keys to values; if you know the key, you can find its value very quickly.

To learn how to use associative arrays, let’s write a program that keeps track of the ages of your friends by name. Enter the program in Listing 16-1.

' AssociativeArray.sb
2 age["Bert"] = 17
3 age["Ernie"] = 16
4 age["Zoe"] = 16
5 age["Elmo"] = 17
6 TextWindow.Write("Enter the name of your friend: ")
7 name = TextWindow.Read()
8 TextWindow.Write(name + " is [")
9 TextWindow.WriteLine(age[name] + "] years old.")

Listing 16-1: Using associative arrays

Lines 2–5 create an associative array named age with four elements in it. You can add more if you’d like, or you can change the array to store the ages of your own friends. Line 6 asks you to enter a friend’s name, and line 7 reads it into the name variable. In line 9, age[name] looks up the age of that friend.

Let’s look at some sample runs of this program:

Enter the name of your friend: Ernie
Ernie is [16] years old.

Enter the name of your friend: ernie
ernie is [16] years old.

Note that the key is case insensitive: it doesn’t matter if you enter age["Ernie"], age["ernie"], or even age["ERNIE"]. If the array contains a key named Ernie, regardless of its case, Small Basic returns the value for that key.

Let’s say you forget which friends’ names you stored in the array, and you try to access the age of someone you forgot to include:

Enter the name of your friend: Grover
Grover is [] years old.

If the array doesn’t contain a certain key, Small Basic returns an empty string, which is why age["Grover"] is empty.

ASSOCIATIVE ARRAYS VS. THE IF/ELSEIF LADDER

In programming, there are usually lots of different ways to approach a particular problem. Here’s another way to write the program like the one in Listing 16-1:

TextWindow.Write("Enter the name of your friend: ")
name = TextWindow.Read()
If (name = "Bert") Then
  age = 17
ElseIf (name = "Ernie") Then
  age = 16
ElseIf (name = "Zoe") Then
  age = 16
ElseIf (name = "Elmo") Then
  age = 17
Else
  age = ""
EndIf
TextWindow.WriteLine(name + " is [" + age + "] years old.")

Although this program seems similar to the one in Listing 16-1, the two have one important difference: here, string comparison is case sensitive. If you enter ernie (with a lowercase e), the program displays the following output:

ernie is [] years old.

The expression If("ernie" = "Ernie") is false. This version of the program is also harder to read and write. When you need to map between a set of keys and values, it’s best to use associative arrays so you don’t have to worry about case.

Putting Associative Arrays to Use

Now that you understand the basics of associative arrays, let’s examine a couple of programs that show you how to use them.

Days in French

The first example translates the days of the week from English to French. This program prompts a user to enter the name of a day in English and outputs that name in French. Enter the code in Listing 16-2.

 1 ' FrenchDays.sb
 2 day["Sunday"] = "Dimanche"
 3 day["Monday"] = "Lundi"
 4 day["Tuesday"] = "Mardi"
 5 day["Wednesday"] = "Mercredi"
 6 day["Thursday"] = "Jeudi"
 7 day["Friday"] = "Vendredi"
 8 day["Saturday"] = "Samedi"
 9
10 TextWindow.Write("Enter the name of a day: ")
11 name = TextWindow.Read()
12 TextWindow.WriteLine(name + " in French is " + day[name])

Listing 16-2: An English-to-French translator

The day array stores the French names for the days of the week (lines 2–8). Each key in the array is the day’s name in English. The program prompts the user to enter the name of a day in English (line 10) and stores the user’s input in the name variable (line 11). The program then looks up the French name using the user’s input as a key, using the syntax day[name], and displays it (line 12). Here’s the output from a sample run:

Enter the name of a day: Monday
Monday in French is Lundi

Do you know any other languages? Change the program to help your friends learn how to say the days of the week in a new language. Feeling sneaky? You could even make up your own secret language!

TRY IT OUT 16-1

What’s the output of Listing 16-2 if the user enters an invalid day name (like Windsday)? Update the program to display an error message when this happens. Use an If statement like this one:

If (day[name] = "") Then
  ' Tell the user they entered a wrong name
Else
  ' Show the French translation
EndIf

Storing Records

Business is booming, and Moe Mows, a local lawn-mowing service in your town, has hired you to write a program that displays the contact information of its customers. When the company enters a customer’s name, the program needs to display the customer’s home address, phone number, and email address. Enter the program in Listing 16-3.

 1 ' MoeMows.sb
 2 address["Natasha"] = "3215 Romanoff Rd"
 3 phone["Natasha"] = "(321) 555 8745"
 4 email["Natasha"] = "blackwidow64@shield.com"
 5
 6 address["Tony"] = "8251 Stark St"
 7 phone["Tony"] = "(321) 555 4362"
 8 email["Tony"] = "ironman63@shield.com"
 9
10 TextWindow.Write("Name of customer: ")
11 name = TextWindow.Read()
12 TextWindow.WriteLine("Address...: " + address[name])
13 TextWindow.WriteLine("Phone.....: " + phone[name])
14 TextWindow.WriteLine("Email.....: " + email[name])

Listing 16-3: Building a simple database

The program uses three associative arrays: address, phone, and email. All three arrays use the customer’s name as a key, and the arrays are used collectively to store customers’ records. A record is a collection of related data items. In this example, each customer’s record has three fields: address, phone, and email. Whether the program has two records or 1,000 records, the search is done the same way. For example, the statement address[name] in line 12 returns the value associated with the key name in the address array. We don’t have to search the address array; Small Basic does this for us, for free!

Here’s the output from a sample run of this program:

Name of customer: Tony
Address...: 8251 Stark St
Phone.....: (321) 555 4362
Email.....: ironman63@shield.com

TRY IT OUT 16-2

Update the program in Listing 16-3 to store the contact information of some of your friends (but not all 500 of your Facebook friends). Add another array that stores the birth date of each friend. You’ll never forget a birthday again!

The Array Object

The Array object in the Small Basic library can help you find important information about the arrays in your programs. In this section, we’ll explore this object in detail and look at some examples on how to use it. To explore the Array object, let’s start by entering the following code:

name = "Bart"         ' An ordinary variable
age["Homer"] = 18     ' An associative array with two elements
age["Marge"] = 17
score[1] = 90         ' An indexed array with one element

This code defines an ordinary variable called name, an associative array called age that has two elements, and an indexed array called score that has one element. You’ll use these arrays in the examples that follow. What can the Array object tell you? Let’s find out!

Is It an Array?

Do you think Small Basic knows that name is an ordinary variable and that age and score are arrays? Run the program in Listing 16-4 to find out.

' IsArray.sb
2 name = "Bart"
3 age["Homer"] = 18
4 age["Marge"] = 17
5 score[1] = 90
6 ans1 = Array.IsArray(name)       ' Returns "False"
7 ans2 = Array.IsArray(age)        ' Returns "True"
8 ans3 = Array.IsArray(score)      ' Returns "True"
9 TextWindow.WriteLine(ans1 + ", " + ans2 + ", " + ans3)

Listing 16-4: Demonstrating the IsArray() method

This code uses the Array object’s IsArray() method. If the variable is an array, this method returns "True"; otherwise, it returns "False". This method shows that the variables age and score are arrays, but the name variable isn’t an array. The IsArray() method can help you to be sure that the variables in your programs are arrays.

How Big Is an Array?

The Array object can also tell you how many elements are stored in your arrays. Run the program in Listing 16-5.

' GetItemCount.sb
2 name = "Bart"
3 age["Homer"] = 18
4 age["Marge"] = 17
5 score[1] = 90
6 ans1 = Array.GetItemCount(name)       ' Returns: 0
7 ans2 = Array.GetItemCount(age)        ' Returns: 2
8 ans3 = Array.GetItemCount(score)      ' Returns: 1
9 TextWindow.WriteLine(ans1 + ", " + ans2 + ", " + ans3)

Listing 16-5: Demonstrating the GetItemCount() method

The GetItemCount() method returns the number of items in the specified array. Note how GetItemCount(name) returns 0, because name isn’t an array. The other two calls return the number of elements in each array. Use GetItemCount() to keep track of how many items you’re storing in an array. You might use this method in a game that allows the player to store items in an inventory and you want to check how many items they have picked up.

Does It Have a Particular Index?

You can also use the Array object to find out whether one of your arrays contains a certain index. To see how, run the program in Listing 16-6.

 1 ' ContainsIndex.sb
 2 age["Homer"] = 18
 3 age["Marge"] = 17
 4 score[1] = 90
 5 ans1 = Array.ContainsIndex(age, 1)       ' Returns "False"
 6 ans2 = Array.ContainsIndex(age, "homer") ' Returns "True"
 7 ans3 = Array.ContainsIndex(age, "Lisa")  ' Returns "False"
 8 TextWindow.WriteLine(ans1 + ", " + ans2 + ", " + ans3)
 9
10 ans1 = Array.ContainsIndex(score, "1")   ' Returns "True"
11 ans2 = Array.ContainsIndex(score, 1)     ' Returns "True"
12 ans3 = Array.ContainsIndex(score, 2)     ' Returns "False"
13 TextWindow.WriteLine(ans1 + ", " + ans2 + ", " + ans3)

Listing 16-6: Demonstrating the ContainsIndex() method

The ContainsIndex() method takes two arguments. The first argument is the name of the array, and the second argument is the index you’re checking for. The method returns "True" or "False" depending on whether the index exists in the array.

Line 6 shows that searching for the index is case insensitive, which is why the search for the index homer returns "True". Also, searching the score array for index "1" (as a string) or index 1 (as a number) both returned "True".

If you’re not sure whether an array includes a particular index, you can use the ContainsIndex() method to find out. This method is especially helpful if you’re working with very long arrays.

Does It Have a Particular Value?

The Array object also offers a method that checks whether an array contains a certain value. Run the program in Listing 16-7 to discover how the ContainsValue() method works.

' ContainsValue.sb
2 age["Homer"] = 18
3 age["Marge"] = 17
4 score[1] = 90
5 ans1 = Array.ContainsValue(age, 18)   ' Returns "True"
6 ans2 = Array.ContainsValue(age, 20)   ' Returns "False"
7 ans3 = Array.ContainsValue(score, 90) ' Returns "True"
8 TextWindow.WriteLine(ans1 + ", " + ans2 + ", " + ans3)

Listing 16-7: Demonstrating the ContainsValue() method

The ContainsValue() method returns "True" or "False" depending on whether the value it checks for exists in the array.

NOTE

Unlike the ContainsIndex() method, the ContainsValue() method is case sensitive. So it’s best to be consistent with your casing!

Give Me All the Indices

Another useful method of the Array object is GetAllIndices(). This method returns an array that has all the indices of a given array. The first element of the returned array has an index of 1. To understand how this method works, run the program in Listing 16-8.

' GetAllIndices.sb
2 age["Homer"] = 18
3 age["Marge"] = 17
4 names = Array.GetAllIndices(age)
5 TextWindow.WriteLine("Indices of the age array:")
For N = 1 To Array.GetItemCount(names)
7   TextWindow.WriteLine("Index" + N + " = " + names[N])
EndFor

Listing 16-8: Demonstrating the GetAllIndices() method

Line 4 calls GetAllIndices() to find all the indices of the age array. This method returns an array, which it saves in the names identifier. The code then starts a loop that runs from the first to the last element in names. Note how the code uses the GetItemCount() method to figure out this value. Here’s the output of this code:

Indices of the age array:
Index1 = Homer
Index2 = Marge

Now let’s put the methods you’ve learned to good use. Do you think your computer is intelligent enough to write poems? Well, let’s see!

TRY IT OUT 16-3

Open the file AnimalSpeed.sb from this chapter’s folder. This game quizzes the player on the top speed (in miles per hour) of different animals. The program has an associative array that looks like this:

speed["cheetah"] = 70
speed["antelope"] = 60
speed["lion"] = 50
' ... and so on

Run this game to see how it works. Which Array object methods does the game use? Explain how the game works, and then come up with some ideas to make the game more fun. Make sure you do all of this assignment. Don’t be a cheetah!

Your Computer the Poet

Now let’s use what we’ve learned about associative arrays to write a program that generates poems. This artificial poet selects words randomly from five lists (article, adjective, noun, verb, and preposition) and combines them into a fixed pattern. To give the poems a central theme, all the words in these lists are related to love and nature. Of course, we might still end up with some silly poetry, but that’s just as fun!

NOTE

The idea of this program is adapted from Daniel Watt’s Learning with Logo (McGraw-Hill, 1983).

Figure 16-1 shows the user interface for the application.

image

Figure 16-1: The user interface for Poet.sb

Every time you click the New button, the poet recites a new poem. Each poem includes three lines that follow these patterns:

• Line 1: article, adjective, noun

• Line 2: article, noun, verb, preposition, article, adjective, noun

• Line 3: adjective, adjective, noun

The following sections guide you through the creation of this program.

Step 1: Open the Startup File

Open the file Poet_Incomplete.sb from this chapter’s folder. The file contains one subroutine named CreateLists(), which creates the five lists you’ll need in this program. This subroutine was added to save you from having to type a whole bunch of words. This is what it looks like:

Sub CreateLists
  article = "1=a;2=the;...;5=every;"
  adjective = "1=beautiful;2=blue;...;72=young;"
  noun = "1=baby;2=bird;...;100=winter;"
  verb = "1=admires;2=amuses;...;92=whispers;"
  prepos = "1=about;2=above;...;37=without;"
EndSub

The ellipses (...) take the place of the missing array elements, but you can see all these elements when you open the file. Note that the article array also includes other determiners, such as one, each, and every.

Step 2: Set Up the Graphical User Interface

Add the code in Listing 16-9 to the beginning of the program file to set up the graphical user interface (GUI) and register the button’s event handler.

 1 GraphicsWindow.Title = "The Poet"
 2 GraphicsWindow.CanResize = "False"
 3 GraphicsWindow.Width = 480
 4 GraphicsWindow.Height = 360
 5 GraphicsWindow.FontBold = "False"
 6 GraphicsWindow.FontItalic = "True"
 7 GraphicsWindow.FontSize = 16
 8
 9 path = Program.Directory
10 GraphicsWindow.DrawImage(path + "\Background.png", 0, 0)
11 Controls.AddButton("New", 10, 10)
12
13 CreateLists()
14
15 artCount = Array.GetItemCount(article)
16 adjCount = Array.GetItemCount(adjective)
17 nounCount = Array.GetItemCount(noun)
18 verbCount = Array.GetItemCount(verb)
19 prepCount = Array.GetItemCount(prepos)
20
21 Controls.ButtonClicked = OnButtonClicked
22 OnButtonClicked()

Listing 16-9: Setting up the GUI

The program starts by initializing the graphics window (lines 1–7), drawing the background image (lines 9–10), and creating the New button (line 11). Next, it calls the CreateLists() subroutine to initialize the five indexed arrays (line 13). Then the program uses the Array object to get the number of items in each array and saves these values in lines 15–19. This way, you can append more elements to the end of these arrays without affecting the rest of the program. For example, if you wanted to add a 73rd adjective, you could add 73=callipygous; within the quotes at the end of the adjectives array line in the CreateLists() subroutine. Because line 16 in Listing 16-9 gets the count of the elements in that array, the new elements you add are automatically counted and randomly selected for the poem, just like the other elements.

Finally, the program registers a handler for the ButtonClicked event (line 21) and calls the handler subroutine to display the first poem (line 22).

Step 3: Respond to Button Clicks

Now you need to add the OnButtonClicked() subroutine, which is shown in Listing 16-10.

 1 Sub OnButtonClicked
 2   GraphicsWindow.DrawImage(path + "\Background.png", 0, 0)
 3
 4   MakeLine1()  ' Constructs poemLine1
 5   MakeLine2()  ' Constructs poemLine2
 6   MakeLine3()  ' Constructs poemLine3
 7
 8   GraphicsWindow.DrawText(180, 140, poemLine1)
 9   GraphicsWindow.DrawText(100, 165, poemLine2)
10   GraphicsWindow.DrawText(180, 190, poemLine3)
11 EndSub

Listing 16-10: The OnButtonClicked() subroutine

This subroutine redraws the background image to clear the graphics window (line 2). It then calls the three subroutines that author the three lines of the poem (lines 4–6) and draws these lines in the graphics window (lines 8–10). Next, you’ll add the three missing subroutines.

Step 4: Write the Poem’s First Line

The poem’s first line is written in this form: article, adjective, noun. Add the subroutine in Listing 16-11, which creates the poem’s first line and assigns it to the poemLine1 variable.

Sub MakeLine1
2   art1 = article[Math.GetRandomNumber(artCount)]
3   adj1 = adjective[Math.GetRandomNumber(adjCount)]
4   noun1 = noun[Math.GetRandomNumber(nounCount)]
5   poemLine1 = art1 + " " + adj1 + " " + noun1
EndSub

Listing 16-11: The MakeLine1() subroutine

The MakeLine1() subroutine selects three random words from the article, adjective, and noun arrays and stores the values in art1, adj1, and noun1 (lines 2–4). It then fills poemLine1 by appending these variables with a whitespace in between them (line 5).

Step 5: Write the Poem’s Second and Third Lines

The MakeLine2() and MakeLine3() subroutines are very similar to the MakeLine1() subroutine. The second line takes this form: article, noun, verb, preposition, article, adjective, noun. The third line takes this form: adjective, adjective, noun. Create these subroutines on your own. If you get stuck, open the file Poet.sb to see how we wrote these subroutines. When you’re done, recite your favorite poem output to your family or friends, and see if they think you wrote it!

TRY IT OUT 16-4

Run your poet program several times to see what a machine poet is capable of authoring. Come up with different poem patterns, and teach this poet how to create them. Then change the words to any words (and any amount of words) that you want! Head to http://tiny.cc/sbpoet/ to share your poem program with the community and to see what others created.

NOTE

The Array object includes three more methods that create a different type of array: SetValue()GetValue()and RemoveValue(). Although these methods work well, the bracketed style of array is more universal among programming languages and is the reason this book focuses on that style.

Programming Challenges

If you get stuck, check out http://nostarch.com/smallbasic/ for the solutions and for more resources and review questions for teachers and students.

1.    Write a program that keeps track of your friends’ phone numbers. Use an associative array that uses your friends’ names as keys; for example, phone["Yoda"] = "555-1138".

2.    Write a program that saves book information. The key is the ISBN of the book. For each book, you need to know the title, the author, and the publication year. Use three associative arrays: title[ISBN], author[ISBN], and year[ISBN].

3.    Open the file VirtualPiano.sb from this chapter’s folder. The program implements a virtual piano using the keyboard. Explain how the program works.