Tables (Arrays)

Tables are very useful. They can be used to store any type of value, including functions or even other tables.

Creating Tables

There are generally two ways to create a table from scratch. The first way uses curly braces to specify a list of values:

my_table = {"apple","orange","peach"};

associative_table = {fruit="apple", vegetable="carrot"}

The second way is to create a blank table and then add the values one at a time:

my_table = {};
my_table[1] = "apple";
my_table[2] = "orange";
my_table[3] = "peach";

associative_table = {};
associative_table.fruit = "apple";
associative_table.vegetable = "carrot";

Accessing Table Elements

Each “record” of information stored in a table is known as an element. Each element consists of a key, which serves as the index into the table, and a value that is associated with that key.

There are generally two ways to access an element: you can use array notation, or dot notation. Array notation is typically used with numeric arrays, which are simply tables where all of the keys are numbers. Dot notation is typically used with associative arrays, which are tables where the keys are strings.

Here is an example of array notation:

t = { "one", "two", "three"};
Dialog.Message("Element one contains:", t[1]);

Here is an example of dot notation:

t = { first="one", second="two", third="three"};
Dialog.Message("Element 'first' contains:", t.first);

Numeric Arrays

One of the most common uses of tables is as arrays. An array is a collection of values that are indexed by numeric keys. In the scripting engine, numeric arrays are one-based. That is, they start at index 1.

Here are some examples using numeric arrays:

Example 1:

myArray = {255,0,255};
Dialog.Message("First Number", myArray[1]);

This would display a dialog message containing the number “255.”

Example 2:

alphabet = {"a","b","c","d","e","f","g","h","i","j","k",
"l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"};
Dialog.Message("Seventh Letter", alphabet[7]);

This would display a dialog message containing the letter “g.”

Example 3:

myArray = {};
myArray[1] = "Option One";
myArray[2] = "Option Two";
myArray[3] = "Option Three";

This is exactly the same as the following:

myArray = {"Option One", "Option Two", "Option Three"};

Associative Arrays

Associative arrays are the same as numerical arrays except that the indexes can be numbers, strings or even functions.

Here is an example of an associative array that uses a last name as an index and a first name as the value:

arrNames = {Anderson="Jason",
            Clemens="Roger",
            Contreras="Jose",
            Hammond="Chris",
            Hitchcock="Alfred"};

Dialog.Message("Anderson's First Name", arrNames.Anderson);

The resulting dialog message would look like this:

Here is an example of a simple employee database that keeps track of employee names and birth dates indexed by employee numbers:

Employees = {}; -- Construct an empty table for the employee numbers

-- store each employee's information in its own table
Employee1 = {Name="Jason Anderson", Birthday="07/02/82"};
Employee2 = {Name="Roger Clemens", Birthday="12/25/79"};

-- store each employee's information table
-- at the appropriate number in the Employees table
Employees[100099] = Employee1;
Employees[137637] = Employee2;

-- now typing "Employees[100099]" is the same as typing "Employee1"
Dialog.Message("Birthday",Employees[100099].Birthday);

The resulting dialog message would look like this:

Using For to Enumerate Tables

There is a special version of the for statement that allows you to quickly and easily enumerate the contents of an array. The syntax is:

for index,value in table do
     operate on index and value
end

For example:

mytable = {"One","Two","Three"};

-- display a message for every table item
for j,k in mytable do
     Dialog.Message("Table Item", j .. "=" .. k);
end

The result would be three dialog messages in a row, one for each of the elements in mytable, like so:

Remember the above for statement, because it is a quick and easy way to inspect the values in a table. If you just want the indexes of a table, you can leave out the value part of the for statement:

a = {One=1,Two=2,Three=3};

for k in a do
     Dialog.Message("Table Index",k);
end

The above script will display three message boxes in a row, with the text “One,” “Three,” and then “Two.”

Whoa there—why aren’t the table elements in order? The reason for this is that internally the scripting engine doesn’t store tables as arrays, but in a super-efficient structure known as a hash table. (Don’t worry, I get confused about hash tables too.) The important thing to know is that when you define table elements, they are not necessarily stored in the order that you define or add them, unless you use a numeric array (i.e. a table indexed with numbers from 1 to whatever).

Copying Tables

Copying tables is a bit different from copying other types of values. Unlike variables, you can't just use the assignment operator to copy the contents of one table into another. This is because the name of a table actually refers to an address in memory where the data within the table is stored. If you try to copy one table to another using the assignment operator, you end up copying the address, and not the actual data.

For example, if you wanted to copy a table, and then modify the copy, you might try something like this:

table_one = { mood="Happy", temperature="Warm" };

-- create a copy
table_two = table_one;

-- modify the copy
table_two.temperature = "Cold";

Dialog.Message("Table one temperature is:", table_one.temperature);
Dialog.Message("Table two temperature is:", table_two.temperature);

If you ran this script, you would see the following two dialogs:

         

Wait a minute...changing the “temperature” element in table_two also changed it in table_one. Why would they both change?

The answer is simply because the two are in fact the same table.

Internally, the name of a table just refers to a memory location. When table_one is created, a portion of memory is set aside to hold its contents. The location (or “address”) of this memory is what gets assigned to the variable named table_one.

Assigning table_one to table_two just copies that memory address—not the actual memory itself.

It’s like writing down the address of a library on a piece of paper, and then handing that paper to your friend. You aren’t handing the entire library over, shelves of books and all...only the location where it can be found.

If you wanted to actually copy the library, you would have to create a new building, photocopy each book individually, and then store the photocopies in the new location.

That’s pretty much how it is with tables, too. In order to create a full copy of a table, contents and all, you need to create a new table and then copy over all of the elements, one element at a time.

Luckily, the for statement makes this really easy to do. For example, here’s a modified version of our earlier example, that creates a “true” copy of table_one.

table_one = { mood="Happy", temperature="Warm" };

-- create a copy
table_two = {};
for index, value in table_one do
    table_two[index] = value;
end

-- modify the copy
table_two.temperature = "Cold";

Dialog.Message("Table one temperature is:", table_one.temperature);
Dialog.Message("Table two temperature is:", table_two.temperature);

This time, the dialogs show that modifying table_two doesn’t affect table_one at all:

         

Table Functions

There are a number of built-in table functions at your disposal, which you can use to do such things as inserting elements into a table, removing elements from a table, and counting the number of elements in a table. For more information on these table functions, please see Program Reference / Actions / Table in the online help.