next up previous contents
Next: Interpolating Functions Inside Double-Quoted Up: References Previous: The ref() Function

Example: Creating a Data Record

Perl's associative arrays (hashes) are extremely useful when it comes to storing information in a way that facilitates easy retrieval. For example, you could store customer information like this:

%record = ( "Name"    => "Jane Hathaway",

            "Address" => "123 Anylane Rd.",

            "Town"    => "AnyTown",

            "State"   => "AnyState",

            "Zip"     => "12345-1234"

);

The %record associative array also can be considered a data record with five members. Each member is a single item of information. The data record is a group of members that relates to a single topic. In this case, that topic is a customer address. And, a database is one or more data records.

Each member is accessed in the record by using its name as the key. For example, you can access the state member by saying $record{"State"}. In a similar manner, all of the members can be accessed.

Of course, a database with only one record is not very useful. By using references, you can build a multiple record array.The code below shows two records and how to initialize a database array.

The record2.pl script below illstrates this:

%recordOne = ( "Name"    => "Jane Hathaway",

               "Address" => "123 Anylane Rd.",

               "Town"    => "AnyTown",

               "State"   => "AnyState",

               "Zip"     => "12345-1234"

);



%recordTwo = ( "Name"    => "Kevin Hughes",

               "Address" => "123 Allways Dr.",

               "Town"    => "AnyTown",

               "State"   => "AnyState",

               "Zip"     => "12345-1234"

);



@database = ( \%recordOne, \%recordTwo );

You can print the address member of the first record like this:

print( %{$database[0]}->{"Address"} . "\n");

which displays:

123 Anylane Rd.

Let's dissect the dereferencing expression in this print statement. Remember to work left to right and always evaluate brackets and parentheses first. Ignoring the print() function and the newline, you can evaluate this line of code in the following way:

The variable declaration in the above example uses three variables to define the data's structure. We can condense the declaration down to one variable as shown below (database.pl):

@database = (    

    { "Name"    => "Jane Hathaway",

      "Address" => "123 Anylane Rd.",

      "Town"    => "AnyTown",

      "State"   => "AnyState",

      "Zip"     => "12345-1234"

    },

    { "Name"    => "Kevin Hughes",

      "Address" => "123 Allways Dr.",

      "Town"    => "AnyTown",

      "State"   => "AnyState",

      "Zip"     => "12345-1234"

    }

);



print(%{$database[0]}->{"Name"} . "\n");

print(%{$database[1]}->{"Name"} . "\n");

This program displays:

Jane Hathaway

Kevin Hughes

Let's analyze the dereferencing code in the first print line.

Even though the structure declarations in the last two examples look different, they are equivalent. You can confirm this because the structures are dereferenced the same way. What's happening here? Perl is creating anonymous associative array references that become elements of the @database array.

In the previous example, each hash had a name-%recordOne and %recordTwo. In the current example, there is no variable name directly associated with the hashes. If you use an anonymous variable in your programs, Perl automatically will provide a reference to it.

We can explore the concepts of data records a bit further using this basic example. So far, we've used hash references as elements of an array. When one data type is stored inside of another data type, this is called nesting data types. You can nest data types as often and as deeply as you would like.

At this stage of the example, %{$database[0]}->{"Name"} was used to dereference the "Name" member of the first record. This type of dereferencing uses an array subscript to tell Perl which record to look at. However, you could use an associative array to hold the records. With an associative array, you could look at the records using a customer number or other id value.

The following code shows how this can be done (database2.pl):

 %database = (

    "MRD-100" => { "Name"    => "Jane Hathaway",

                   "Address" => "123 Anylane Rd.",

                   "Town"    => "AnyTown",

                   "State"   => "AnyState",

                   "Zip"     => "12345-1234"

                 },

    "MRD-250" => { "Name"    => "Kevin Hughes",

                   "Address" => "123 Allways Dr.",

                   "Town"    => "AnyTown",

                   "State"   => "AnyState",

                   "Zip"     => "12345-1234"

                 }

);



print(%{$database{"MRD-100"}}->{"Name"} . "\n");

print(%{$database{"MRD-250"}}->{"Name"} . "\n");

This program displays:

Jane Hathaway

Kevin Hughes

You should be able to follow the same steps that we used previously to decipher the print statement in this listing. The key is that the associative array index is surrounded by the curly brackets instead of the square brackets used previously.

There is one more twist that I would like to show you using this data structure. Let's see how to dynamically add information. First, we'll look at adding an entire data record, and then we'll look at adding new members to an existing data record. The nex code fragment shows you can use a standard hash assignment to dynamically create a data record.

The database3.pl script does this:

$database{"MRD-300"} = {

    "Name"    => "Nathan Hale",

    "Address" => "999 Centennial Ave.",

    "Town"    => "AnyTown",

    "State"   => "AnyState",

    "Zip"     => "12345-1234"

};



$refCustomer = $database{"MRD-300"};

print(%{$refCustomer}->{"Name"} . "\n");

print(%{$refCustomer}->{"Address"} . "\n");

This program displays:

Nathan Hale

999 Centennial Ave.

Notice that by using a temporary variable ($refCustomer), the program code is more readable. The alternative would be this:

print(%{$database{"MRD-300"}}->{"Name"} . "\n");

Most programmers would agree that using the temporary variable aids in the understanding of the program.

Our last data structure example will show how to add members to an existing customer record. The final code listing (database4.pl) shows how to add two phone number members to customer record MRD-300.

$codeRef = sub {

    while (($key, $value) = each(%database)) {

        print("$key = {\n");

        while (($innerKey, $innerValue) = each(%{$value})) {

            print("\t$innerKey => $innerValue\n");

        }

        print("};\n\n");

    }

};



$database{"MRD-300"} = {

    "Name"    => "Nathan Hale",

    "Address" => "999 Centennial Ave.",

    "Town"    => "AnyTown",

    "State"   => "AnyState",

    "Zip"     => "12345-1234"

};



# print database before dynamic changes.

&{$codeRef};



$refCustomer = $database{"MRD-300"};

%{$refCustomer}->{"Home Phone"}     = "(111) 511-1322";

%{$refCustomer}->{"Business Phone"} = "(111) 513-4556";



# print database after dynamic changes.

&{$codeRef};

This program displays:

MRD-300 = {

        Town => AnyTown

        State => AnyState

        Name => Nathan Hale

        Zip => 12345-1234

        Address => 999 Centennial Ave.

};



MRD-300 = {

        Town => AnyTown

        State => AnyState

        Name => Nathan Hale

        Home Phone => (111) 511-1322

        Zip => 12345-1234

        Business Phone => (111) 513-4556

        Address => 999 Centennial Ave.

};

This example does two new things. The first thing is that it uses an anonymous function referenced by $codeRef. This is done for illustration purposes. There is no reason to use an anonymous function. There are actually good reasons for you not to do so in normal programs. I think that anonymous functions make programs much harder to understand.

Note When helper functions are small and easily understood, I like to place them at the beginning of code files. This helps me to quickly refresh my memory when coming back to view program code after time spent doing other things.

The second thing is that a regular hash assignment statement was used to add values. You can use any of the array functions with these nested data structures.


next up previous contents
Next: Interpolating Functions Inside Double-Quoted Up: References Previous: The ref() Function
dave@cs.cf.ac.uk