Hash Functions

In this second part of a two-part series on hashes in Perl, you’ll learn about hash functions and hashes in different contexts. This article is excerpted from chapter five of the book Beginning Perl, written by James Lee (Apress; ISBN: 159059391X).

Hash in List Context

When we discussed lists and arrays, we spent a lot of time talking about the difference between list and scalar context. Let’s look at what happens when we evaluate a hash in list context. This is demonstrated with the following program:

#!/usr/bin/perl -w
# listcontext.pl

use strict;

my %person = (
    name  => ‘John Doe’,
    age   => 39,
    phone => ’555-1212′,
    city  => ‘Chicago’
);

my @data = %person;

print "list context: ", join("|", @data), "n";
print "another way: ", %person, "n";

This program takes the hash in list context in two ways. First, it assigns it to an array:

my @data = %person;

then the array is printed by joining its contents with the string “|” (more on the join() function in Chapter 7):

print "list context: ", join("|", @data), "n";

The second way is to simply print it:

print "another way: ", %person, "n";

Recall that all arguments to the print() function are treated in list context.

When executed, we can see that a hash variable in list context is a list of the key/value pairs in the order stored in memory (not necessarily in the order in which the hash was created):

$ perl listcontext.pl
list context: age|39|city|Chicago|phone|555-1212|name|John Doe
another way: phone555-1212age39cityChicagonameJohn Doe
$

We see a key (phone), followed by its value (555-1212), followed by a key (age), followed by its value (39), etc.

{mospagebreak title=Hash in Scalar Context}

A hash in scalar context is shown in this example:

#!/usr/bin/perl -w
# scalarcontext.pl

use strict;

my %person = (
    name  => ‘John Doe’,
    age   => 39,
    phone => ’555-1212′,
    city  => ‘Chicago’
);

my $scalar = %person;

print "scalar context: $scalarn";

if (%person) {
    print "%person has at least one key/value pairn";
} else {
    print "%person is empty!n";
}

Executing this program produces the following:

$ perl scalarcontext.pl
scalar context: 3/8
%person has at least one key/value pair
$

This code produces an unexpected result. The following code:

my $scalar = %person;

print "scalar context: $scalarn";

prints the string “scalar context: 3/8”. Therefore, this hash in scalar context is “3/8” which means that we are using three buckets, or memory locations, out of eight buckets allocated.

This string is not so interesting unless we notice that the string “3/8” is a true value in Perl. Also, if our hash was empty, its value in scalar context would be the empty string, "". So a hash in scalar context is normally treated as a true/false value—true if there is anything in it, false if empty:

if (%person) {
    print "%person has at least one key/value pairn";
} else {
   
print "%person is empty!n";
}

{mospagebreak title=Hash Functions}

Since hashes in list context are apparently random collections of key/value pairs, we can’t really use foreach loops on them directly. If we did, we would get both keys and values with no indication as to which was which. To help us, Perl provides three functions for iterating over hashes: keys() , values() , and
each() .

Also, Perl provides functions to remove elements ( delete() , already seen previously), and to check to see if a key exists in the hash ( exists() ).

The keys() Function

First, there is keys(%hash). This gives us a list of the keys (all of the scalars on the left-hand side). This is usually what we want when we wish to visit each hash entry in turn as shown in this example:

#!/usr/bin/perl -w
# keys.pl

use strict;

my %where = (
        Gary     => "Dallas",
        Lucy     => "Exeter",
        Ian      => "Reading",
        Samantha => "Oregon"
);

foreach (keys %where) {
    print "$_ lives in $where{$_}n";
}

Currently, this tells us

$ perl keys.pl
Lucy lives in Exeter
Samantha lives in Oregon
Gary lives in Dallas
Ian lives in Reading
$

You may find that the output appears in a different order on your machine.1 Don’t worry. As mentioned before, hashes are unordered, and there’s no guarantee that the keys will come out in the same order each time. It really depends on the particular version of Perl that you are using.

Let’s look at the part of the program that does all the work:

foreach (keys %where) {
    print "$_ lives in $where{$_}n";
}

keys() is a function which, like sort() and reverse() , returns a list. The list in this case is
qw(Lucy Samantha Gary Ian) , and the foreach loop visited each of those values in turn. As $_ was set to each one, we could print the name and look up that entry in the hash.

The values() Function

The counterpart to keys() is values() , which returns a list of all of the values in the hash. This is somewhat less useful, since you can always find the value if you have the key, but you cannot easily find the key if you have the value. It’s almost always advantageous to use keys() instead.

Here is an example using the values() function:

#!/usr/bin/perl -w
# values.pl

use strict;

my %where = (
        Gary     => "Dallas",
        Lucy     => "Exeter",
       
Ian      => "Reading",
        Samantha => "Oregon"
);

foreach (values %where) {
    print "someone lives in $_n";
}

1. Or even different every time that you run it! Some 5.8.x Perl installations have hash order randomization turned on by default.

Executing this program produces the following:

$ perl values.pl
someone lives in Exeter
someone lives in Oregon
someone lives in Dallas
someone lives in Reading
$

The each() Function

The next hash function is each(). It returns each hash entry as a key/value pair. Normally, the values returned are copied into an assignable list like this:

each() . It returns hash entry as a key/value pair. Normally, the values returned are copied into an assignable list like this: The next hash function is each(). It returns hash entry as a key/value pair. Normally, the values returned are copied into an assignable list like this:

($k, $v) = each %where;

This is illustrated in each.pl :

#!/usr/bin/perl -w
# each.pl

use strict;

my %where = (
        Gary     => "Dallas",
        Lucy     => "Exeter",
        Ian      => "Reading",
        Samantha => "Oregon"
);

my($k, $v);
while (($k, $v) = each %where) {
    print "$k lives in $vn";
}

Here is an example of this program executing:

$ perl each.pl
Lucy lives in Exeter
Samantha lives in Oregon
Gary lives in Dallas
Ian lives in Reading
$

The delete() Function

We have already seen the delete() function. It removes a key/value pair from a hash. This statement from badhash.pl removes the pair Lucy/Exeter from
%where :

delete $where{Lucy};

Since we are on the subject, we should mention that the delete() function also deletes array elements. The following code would remove element 3 from the array @array . Note that the element returns to an uninitialized state:

delete $array[3];

The exists() Function

The last function we will look at is the exists() function. It returns true if the key exists in the hash, false if not. Here is an example:

exists() function. It returns true if the key exists in the hash, false if not. Here is an example: The last function we will look at is the exists() function. It returns true if the key exists in the hash, false if not. Here is an example:

#!/usr/bin/perl -w
# exists.pl


use strict;

my %where = (
        Gary     => "Dallas",
        Lucy     => "Exeter",
        Ian      => "Reading",
       
Samantha => "Oregon"
);

print "Gary exists in the hash!n" if exists $where{Gary};
print "Larry exists in the hash!n" if exists $where{Larry};

Running this program results in the following:

$ perl exists.pl
Gary exists in the hash!
$


Note  exists() returns 1 when true, an empty string when false.


The exists() function also works for array elements. This code checks to see if element 3 exists in @array :

if (exists $array[3]) {
   
print "element 3 exists!n";
}

{mospagebreak title=Hash Examples}

Hashes are very useful variables and there are many uses for them. Here are a few examples of using hashes to solve common problems.

Creating Readable Variables

The most basic use of a hash is to be able to index into a variable to obtain information using a readable string which is far more user-friendly than using a numeric index as we would with an array. For instance, this program shows that we can create a record of strings representing RGB colors that one might find in an HTML page:

#!/usr/bin/perl -w
# colors.pl

use strict;

my %colors = (
   red    => ‘#FF0000′,
   green  => ‘#00FF00′,
   blue   => ‘#0000FF’,
   white  => ‘#FFFFFF’,
   black  => ‘#000000′,
  
purple => ‘#520063′
);

print "Red is:    $colors{red}n";
print "Blue is:   $colors{blue}n";
print "Purple is: $colors{purple}n";

Notice how the information in the hash is laid out in such a way that it is readable by human beings. It is easy to see that the RGB string for “red” is “#FF0000” and indexing into the hash is the human-friendly $colors{red} .

Executing this code produces the following:

$ perl colors.pl
Red is:    #FF0000
Blue is:   #0000FF
Purple is: #520063
$

“Reversing” Information

Recall the hash we created earlier in this chapter that was a collection of people and where they lived:

%where = (
        Gary     => "Dallas",
        Lucy     => "Exeter",
        Ian      => "Reading",
        Samantha => "Oregon"
);

If you need to turn this hash around to look up people by where they live, you can use a hash in list context that produces a list of key/value pairs, reverse the list with the reverse() function, and then assign it to a new hash.

%who = reverse %where;

Be careful though—if you have two values that are the same, then converting them to keys means that one will be lost. Remember that keys must be unique.

Here is a program illustrating reversing a hash:

#!/usr/bin/perl -w
# reverse.pl

use strict;

my %where = (
    Gary     => "Dallas",
    Lucy     => "Exeter",
    Ian      => "Reading",
    Samantha => "Oregon"
);

my %who = reverse %where;

foreach (keys %who) {
    print "in $_ lives $who{$_}n";
}

Executing this code produces the following:

$ perl reverse.pl
in Oregon lives Samantha
in Exeter lives Lucy
in Reading lives Ian
in Dallas lives Gary
$

After we assigned to %who , we created a hash indexed by the location producing the name that is the direct opposite of %where , which was indexed by name to produce the location.

{mospagebreak title=Counting Things}

A very common use of a hash variable is to count things. For instance, we can count the number of characters in a string or the items in an array. Let’s look at counting items in an array.

We will create an array of names and then we will count the number of times each name occurs in the array. For instance, for this array:

my @names = qw(
    John   Sue    Larry
    Mary   John   Mary
    Larry  John   Joe
    Lisa   John   Mary
);

we see that @names is a collection of 12 names. Upon close inspection, we see that “John” occurs four times, “Sue” occurs once, and so on.

We can use a hash to keep a count of the number of times a name occurs in @names by creating a hash that will have the names as its keys, and the number of occurrences of the name as the value associated with the key. For instance, when all the names in @names are processed, we will end up with a hash that resembles

John  => 4,
Sue   => 1,
Larry => 2,
Mary  => 3,
Joe   => 1,
Lisa  => 1

Here is a program illustrating this concept:

#!/usr/bin/perl -w
# count.pl

use strict;

my @names = qw(
    John   Sue    Larry
    Mary   John   Mary
    Larry  John   Joe
   
Lisa   John   Mary
);

my %count;

foreach (@names) {
    if (exists $count{$_}) {
        $count{$_}++;
    } else {
        $count{$_} = 1;
    }
}

foreach (keys %count) {
    print "$_ toccurs $count{$_} time(s)n";
}

Executing this code produces the following result:

$ perl count.pl
Joe      occurs 1 time(s)
Lisa     occurs 1 time(s)
John     occurs 4 time(s)
Mary     occurs 3 time(s)
Sue      occurs 1 time(s)
Larry    occurs 2 time(s)
$

The most important part of this program is when we loop through the array and keep count:

foreach (@names) {
    if (exists $count{$_}) {
        $count{$_}++;
    } else {
        $count{$_} = 1;
    }
}

This code implements the logic “For each name in the array, if the name already exists in the hash, then increment the value by 1 (incrementing the count); else if it does not exist in the hash, then add the name to the hash with the initial value of 1.” After all the names are processed, then the hash will contain all the names and the number of times that each name is present in @names .

For minimalists, the if statement can be shortened because this logic:

if (exists $count{$_}) {
    $count{$_}++;
} else {
    $count{$_} = 1;
}

is built into the statement

$count{$_}++;

Therefore, our foreach loop could be changed to

foreach (@names) {
    $count{$_}++;
}

or more simply

$count{$_}++ foreach @names;

Summary

Hashes are unordered structures made up of pairs, each pair consisting of a key and a value, and given the key we can look up the value. Generally,
$hash{$key} = $value. We can loop over all the elements of a hash by processing the keys using a foreach loop to go through the keys.

Hashes are very useful variables that allow us to create data that is human-readable, reversible, and often used for counting things.

Exercises

  1. Create this hash variable:

    scalar => ‘dollar sign’,
    array  => ‘at sign’,
    hash   => ‘percent sign’

    Process it with a foreach loop that prints the key/value pairs so that the keys are printed in sorted order:

    array: at sign
    hash: percent sign
    scalar: dollar sign

  2. Store your important phone numbers in a hash. Write a program to look up numbers by the person’s name.
  3. Turn the joke machine program in Chapter 4 from two arrays into one hash. While doing so, write some better lightbulb jokes.

 

 

[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan