[JaxPM] syslog analyzing

Bill Jones bill at fccj.org
Fri Apr 7 20:20:37 CDT 2000


On the jacksonville-pm-list; Jax.PM'er Bill Jones <bill at fccj.org> wrote -

Perl Cookbook:  5.4. Traversing a Hash

Problem

You want to perform an action on each entry (i.e., each key-value pair) in a
hash.

Solution

Use each with a while loop:

while(($key, $value) = each(%HASH)) {
    # do something with $key and $value
}

Or use keys with a foreach loop, unless the hash is potentially very large:

foreach $key (keys %HASH) {
    $value = $HASH{$key};
    # do something with $key and $value
}

Discussion

Here's a simple example, iterating through the %food_color hash from the
introduction.

# %food_color per the introduction
while(($food, $color) = each(%food_color)) {
    print "$food is $color.\n";
}
Banana is yellow.
Apple is red.
Carrot is orange.
Lemon is yellow.

foreach $food (keys %food_color) {
    my $color = $food_color{$food};
    print "$food is $color.\n";
}
Banana is yellow.
Apple is red.
Carrot is orange.
Lemon is yellow.

We didn't really need the $color variable in the foreach example because we
only use it once. Instead, we could have just written:

print "$food is $food_color{$food}.\n"

Every time each is called on the same hash, it returns the "next" key-value
pair. We say "next" because the pairs are returned in the order the
underlying lookup structure imposes on them, and this order is almost never
alphabetic or numeric. When each runs out of hash elements, it returns the
empty list (), which tests false and terminates the while loop.

The foreach example uses keys, which constructs an entire list containing
every key from hash, before the loop even begins executing. The advantage to
using each is that it gets the keys and values one pair at a time. If the
hash contains many keys, not having to pre-construct a complete list of them
can save substantial memory. The each function, however, doesn't let you
control the order in which pairs are processed.

Using foreach and keys to loop over the list lets you impose an order. For
instance, if we wanted to print the food names in alphabetical order:

foreach $food (sort keys %food_color) {
    print "$food is $food_color{$food}.\n";
}
Apple is red.
Banana is yellow.
Carrot is orange.
Lemon is yellow.

This is a common use of foreach. We use keys to obtain a list of keys in the
hash, and then we use foreach to iterate over them. The danger is that if
the hash contains a large number of elements, the list returned by keys will
use a lot of memory. The trade-off lies between memory use and the ability
to process the entries in a particular order. We cover sorting in more
detail in Recipe 5.9.

Because keys, values, and each all use the same internal data structures, be
careful about mixing calls to these functions or prematurely exiting an each
loop. Each time you call keys or values, the current location for each is
reset. This code loops forever, printing the first key returned by each:

while ( ($k,$v) = each %food_color ) {
    print "Processing $k\n";
    keys %food_color;               # goes back to the start of %food_color
}

Modifying a hash while looping over it with each or foreach is, in general,
fraught with danger. The each function can behave differently with tied and
untied hashes when you add or delete keys from a hash. A foreach loops over
a pre-generated list of keys, so once the loop starts, foreach can't know
whether you've added or deleted keys. Keys added in the body of the loop
aren't automatically appended to the list of keys to loop over, nor are keys
deleted by the body of the loop deleted from this list.

Example 5.1 reads a mailbox file and reports on the number of messages from
each person. It uses the From: line to determine the sender. (It isn't smart
in this respect, but we're showing hash manipulation, not mail-file
processing.) Supply the mailbox filename as a command-line argument, or use
"-" to indicate you're piping the mailbox to the program.

Example 5.1: countfrom

#!/usr/bin/perl
# countfrom - count number of messages from each sender

$filename = $ARGV[0] || "-";

open(FILE, "<$filename")            or die "Can't open $filename : $!";

while(<FILE>) {
    if (/^From: (.*)/) { $from{$1}++ }
}

foreach $person (sort keys %from) {
    print "$person: $from{$person}\n";
}

See Also

The each and keys functions in perlfunc (1) and in Chapter 3 of Programming
Perl; we talk about for and foreach in Recipe 4.5

- FCCJ * 501 W State St * Jacksonville, Fl 32202 * 904/632-3089 -

> From: Nate <nate at campin.net>
> Date: Fri, 7 Apr 2000 08:17:01 -0700 (PDT)
> To: Bill Jones <bill at fccj.org>
> Cc: Jax Perl Mongers <jacksonville-pm-list at happyfunball.pm.org>
> Subject: Re: [JaxPM] syslog analyzing
> 
> On the jacksonville-pm-list; Jax.PM'er Nate <nate at campin.net> wrote -
> 
> How do I iterate over this list of hashes?


Jax.PM Moderator's Note:
This message was posted to the Jacksonville Perl Monger's Group listserv.
The group manager can be reached at -- owner-jacksonville-pm-list at pm.org
to whom send all praises, complaints, or comments...




More information about the Jacksonville-pm mailing list