Jason Normandin wrote:

> Hey Group
>
> I am new to hashes, so please be kind : )
>
> I am trying to create a hash of hash's with the following characteristics:
>
> 1. The outer hash has a unique key called $ELEMENT

Back off here.  You haven't yet described the problem you are trying to solve,
and you are already making wild guesses about the tools to be used for the
problem.  You get nowhere, and get there in a hurry, when you act begfore you
have identified a goal.

>
> 2. The value of the outer hash is a key to the innner hash called $DATE
> 3. The value of the inner hash is a value called $ERROR
>
> I need to loop through some data which has multipe $DATE and $ERROR entries
> per $ELEMENT and add them to the $ELEMENT hash based on the $ELEMENT value.
>
> Eg. Element Fred would have multiple DATE entries. Each date would have one
> error.
>
> I then need to iterate through the hash and display the element name( only
> once if possible ), each timestamp and error.
>
> Ex.
>
> ELEMENT DATE            ERROR
> jason           Jun 1, 2003     No Data
>                 Jun 4,2003      No Response
>                 July 1,2003     No Data
> fred            Jan 2, 2002     No Response
>                 Jany 4,2002     Illegal value
> ....
> ..
>
> This is just a dummy example. The real error types range and are unique.
>
> Can anyone help with my constructing such a hash ?
>
> I tried the following, which sucessfully creates the outerhash, but only
> populates one value for each date, error pair:
>
>         $snmpErr{$ELEMENT}=
>         {
>                 name => $ELEMENT,

No need for this.  You already have the significant information stored in the
key.  That redundancy will hold true for every single element in the list.

>
>                 date => $DATE,
>                 error => $ERROR
>         };

Presuming that you are reading this, you should probably be assigning smething
more like:

$snmpErr{$element}->{ error_guid} = [$date, $error];

Unless, of course, you know for sure, that no person will ever make more than
one error in a single day.

>
> I then loop through via:
>
>         foreach my $entry (keys %snmpErr)
>         {
>                 $el = $snmpErr{$entry}{name};

 $snmpErr{$entry}->{'name'} was assigned the same way as the other two keys.
Why are you accessing it differently? $snmpErr{$entry} is a hash reference, not
a hash.  If you do want a subsort by name, just skip the name key entirely,
since it's reduncant.

>                 $el = $entry;
>                 $ts = $snmpErr{$entry}->{date};
>                 $er = $snmpErr{$entry}->{error};
>         }
>
> How far off am I ? : )
>
> Thanks for the help all !!
>
> - Jason

I'd start by thinking about any one error.  From you data, it sounds like the
distinguishing characteristics are the element [Do you often refer to living
beings as elements?  That seems very, very sad.  OTOH, if you simply give your
machines human names, its sorta sweet], the date, and the error message.

You indicated that you want to group by element, so lets not put that
information in the structure used to hold any one error.  You have achoice of
two suitable structures here, and its a matter of taste.  Using an array where
the elements are alway added in the same order could work alright.  Still you
would have to remember that $error[0] is the date and $error[1] was the error
string.  Or was it the other way around?

my $error = [$date, $description]
creates a reference to an anonyous array with element ditinguished by their
order.  Note that you have to use a reference here.  Whole arrays or hashes get
flattened into simple lists when assigned to elements of multidimesional
structures.  The results are very ugly.

I would recommend using a hash instead, for the sake of clarity.  It is less
efficient, but it is more important to be clear.

my $error = {
    date => $name,
    description => $description
}

The file snippet you showed indicated that all the error for any element were
already group by the element.  That is convenient.  You can make some use of
that in the next layer, which should be an array.  Since any element could have
an arbitrary number of errors associated with it, and since there is no
meaningful key to be associated with any element to distinguish it, an
anonymous array is appropriate, IMHO. for this level of structure.
my $elemental_errors = [
    {date => $date[0], description => $description[0]},
    {date => $date[1], description => $description[1]},
...
although you would not want to load it that way. Or maybe you would.

If you are committing this to a database, this advice might change.  You might
then want to use the guid of the item in the "error" table as the key for each
error item.  On that case would want to make a hash.

my $elemental_errors = {
    $db_primary_keys[0] => {date => $date[0], description => $description[0]},
    $db_primary_keys[1] => {date => $date[1], description => $description[1]},

So we now need to find the appropriate type of collection to hold the
elements.  Each of these clearly does have a name, which, presuming that the
names are guaranteed unique, a perfect call for a hash.

my $errors = {
   'Alex'    =>    [{date => '1/1/2003', error => 'engaging in ultraviolence'},

                    {date => '10/31/2003', error => 'submittingly cravenly to
dictates of society'}]
   'Dim'     =>    [...

So if you put all this together, and assuming that you read from a file
organized by element, we might have:


use strict;
use warnings;

use Data::Dumper;

my $log;
open $log, 'errors.log' or die "Sorry, couldn't open the error log. $!";
my $errors = {};
my $error_line = <$log>;   #we throw this first line away
$error_line = <$log>;
my $element;
chomp $error_line;
while ($error_line) {
    if ($error_line =~ /^\w/) {
        $error_line =~ /(.*?)\s+([A-Z][a-z]{2,3}\s+\d+,\s*\d+)\s+?\b(.+)\b/;
        $element = $1;
        $errors->{$element} = [];
        push @{$errors->{$element}}, trim_details($2, $3);
    } else {
        $error_line =~ /\s+([A-Z][a-z]{2,3}\s+\d+,\s*\d+)\s+?\b(.+)\b/;
        push @{$errors->{$element}}, trim_details($1, $2);
    }
    $error_line = <$log>;
}

sub trim_details {
    my ($date, $error) = @_;

    $date =~ s/\s+/ /g;
    $date =~ s/,\b/, /;
    return {date => $date, error => $error};
}

print Dumper $errors;
^Z
$VAR1 = {
          'jason' => [
                       {
                         'date' => 'Jun 1, 2003',
                         'error' => 'No Data'
                       },
                       {
                         'date' => 'Jun 4, 2003',
                         'error' => 'No Response'
                       },
                       {
                         'date' => 'July 1, 2003',
                         'error' => 'No Data'
                       }
                     ],
          'fred' => [
                      {
                        'date' => 'Jan 2, 2002',
                        'error' => 'No Response'
                      },
                      {
                        'date' => 'Jany 4, 2002',
                        'error' => 'Illegal value'
                      }
                    ]
        };

HTH,

Joseph


-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to