[Melbourne-pm] Hash Containing Array

Jacinta Richardson jarich at perltraining.com.au
Sat Mar 21 18:48:26 PDT 2009


I've expanded the code a little to include some scaffolding, so I'm 
working with:

##########################################
package Foo;
use strict;
use warnings;

sub new {
     my $class = shift;
     my $self  = {};
     $self->{_DATASET}         = undef;
     $self->{_RULE}            = undef;
     $self->{_PRIVILEGE}       = undef;
     $self->{_ACC_VIA_RULE}    = {};
     $self->{_ACC_VIA_PRIV}    = [];
     $self->{_CAN_UPDATE_RULE} = [];
     bless( $self, $class );
     return $self;
}

sub set_acc_via_rule {
     my $self = shift;
     my $key  = shift;
     my @val  = @_;
     foreach my $val (@val) {
         unshift @{ $self->{_ACC_VIA_RULE}{$key} }, $val;
     }
     return;
}

sub get_back_keys {
     my $self = shift;
     my ( $key, $value );
     while ( my ( $key, $value ) = each( $self->{_ACC_VIA_RULE} ) ) {
         unshift @keys, $key;
     }
     return @keys;
}


package main;
use strict;
use warnings;
use Data::Dumper;

my $foo = Foo->new();

$foo->set_acc_via_rule("bar", (1..10));
$foo->set_acc_via_rule("baz", ('a'..'j'));

print Dumper $foo;
##########################################

This still yields the same error you were getting:


Type of arg 1 to each must be hash (not hash element) at test.pl line 
31, near "} ) "

and also:

Global symbol "@keys" requires explicit package name at test.pl line 32.
Global symbol "@keys" requires explicit package name at test.pl line 34.

because you never declare @keys in your get_back_keys method.


If we comment out get_back_keys and run this we get the following 
Data::Dumper structure:

$VAR1 = bless( {
                  '_RULE' => undef,
                  '_CAN_UPDATE_RULE' => [],
                  '_PRIVILEGE' => undef,
                  '_ACC_VIA_PRIV' => [],
                  '_ACC_VIA_RULE' => {
                                       'bar' => [
                                                  10,
                                                  9,
                                                  8,
                                                  7,
                                                  6,
                                                  5,
                                                  4,
                                                  3,
                                                  2,
                                                  1
                                                ],
                                       'baz' => [
                                                  'j',
                                                  'i',
                                                  'h',
                                                  'g',
                                                  'f',
                                                  'e',
                                                  'd',
                                                  'c',
                                                  'b',
                                                  'a'
                                                ]
                                     },
                  '_DATASET' => undef
                }, 'Foo' );


Now I'm guessing from your code that what you want is a method that just 
returns 'bar' and 'baz'.  For this, we don't need to use each, because 
we don't care about the values, only the keys.  Thus we can write:

sub get_back_keys {
     my $self = shift;
     my @keys;

     foreach my $key ( keys %{ $self->{_ACC_VIA_RULE} } ) {
             unshift @keys, $key;
     }
     return @keys;
}

The main mistake you were making was not telling Perl to dereference 
your hash.  When you wrote:

     while ( my ( $key, $value ) = each( $self->{_ACC_VIA_RULE} ) )

you were passing a hash _reference_ to each when each expected a hash. 
To dereference this into a hash we need to change:

	$self->{_ACC_VIA_RULE}

to:

	%{ $self->{_ACC_VIA_RULE} }

which is what I did so that I passed the hash to keys, not the hash 
reference.  Making this small change in your code (and adding "my 
@keys;" makes your code work).

You are using "unshift" a lot, where most people would use "push". 
unshift adds something to the start of the array, whereas push adds it 
to the end of the array.  The syntax is the same.  I realise this might 
be a coding requirement, but if not, it's worth being aware that most 
people will expect to see push rather than unshift.  The following two 
snippets achieve the same result:

     foreach my $val (@val) {
         unshift @{ $self->{_ACC_VIA_RULE}{$key} }, $val;
     }

     foreach my $val (reverse @val) {
         push @{ $self->{_ACC_VIA_RULE}{$key} }, $val;
     }

By all means use unshift if it does what you require!

All the best,

	Jacinta



More information about the Melbourne-pm mailing list