27

I have an array like so:

@switch_ports = ()

and then want to add 50 instances of this hash, to the switch_ports array.

%port = (data1 => 0, data2 => 0, changed => 0)

However, if I push my hash to the array:

push(@switch_ports, %port)

and I do print @switch_ports, I just see:

data10data20changed0

so it just seems to be adding them to the array, (joining them) and if I try and loop the array and print the keys, it also fails.

  1. Can you store a hash in an array?

  2. Can you have an array of hashes?

I'm trying to get this:

switchports
    0
        data1
        data2
        changed
    1
        data1
        ....

thus:

foreach $port (@switchport) {
    print $port['data1']
}

would return all of the data1 for all of the hashes in the array.

4 Answers 4

48

In Perl, array and hash members must be a single value. Before Perl 5.0, there was no (easy) way to do what you want.

However, in Perl 5 you can now use a reference to your hash. A reference is simply the memory location where the item is being stored. To get a reference, you put a backslash in front of the variable:

use feature qw(say);

my $foo = "bar";
say $foo;    #prints "bar"
say \$foo;   #prints SCALAR(0x7fad01029070) or something like that

Thus:

my @switch_ports = ();
my %port = ( data1 => 0, data2 => 0, changed => 0 );
my $port_ref = \%port;

push( @switch_ports, $port_ref );

And, you don't have to create $port_ref:

my @switch_ports = ();
my %port = ( data1 => 0, data2 => 0, changed => 0 );

push( @switch_ports, \%port );

To get the actual value of the reference, simply put the symbol back on front:

#Remember: This is a REFERENCE to the hash and not the hash itself
$port_ref = $switch_ports[0];
%port = %{$port_ref};      #Dereferences the reference $port_ref;

print "$port{data1}  $port{data2}  $port{changed}\n";

Another shortcut:

%port = %{$port[0]};   #Dereference in a single step
print "$port{data1}  $port{data2}  $port{changed}\n";

Or, even shorter, dereferencing as you go along:

print ${$port[0]}{data1} . " " . ${$port[0]}{data2} . " " . ${$port[0]}{changed} . "\n";

And a little syntactic sweetener. It means the same, but is easier to read:

print $port[0]->{data1} . " " . $port[0]->{data2} . " " . $port[0]->{changed} . "\n";

Take a look at Perldoc's perlreftut and perlref. The first one is a tutorial.

Sign up to request clarification or add additional context in comments.

2 Comments

Perl 5.000 was released on October 17, 1994. I think that by 2011, 17 years later, disclaimers about Perl4 behavior are no longer very necessary :)
You'd think this would be the case. However, there's a lot of Perl tutorials out there still stuck in ancient Perl 3.x syntax: If it was good enough for Grandpa, it's good enough for me!. I still see people using require "find.pl"; instead of use File::Find; and chop instead of chomp. Even many modern Learn Perl books never get into references.
11

When you try:

%port = (data1 => 0, data2 => 0, changed => 0);
push @switch_ports, %port;

What really happens is:

push @switch_ports, "data1", 0, "data2", 0, "changed", 0;

Because arrays and hashes will automatically break into their elements when used in list context.

When you want to create 50 instances of a hash, it is not a good idea to use a reference to an existing hash as others have suggested, as that will only create 50 different references to the same hash. Which will crash and burn for obvious reasons.

What you need is something like:

push @array, { data1 => 0, data2 => 0, changed => 0 } for 1 .. 50;

Which will add 50 unique anonymous hashes to the array. The braces denotes construction of an anonymous hash, and returns a scalar reference to it.

ETA: Your example of how to access this data is wrong.

foreach $port (@switchport) {
    print $port['data1'];    # will use @port, not $port
}

Using a subscript on a scalar variable will attempt to access an array in that namespace, not a scalar. In perl, it is valid to have two separate variables $port and @port. Brackets are used for arrays, not hashes. When using references, you also need to use the arrow operator: $port->{data1}. Hence:

for my $port (@switchport) {
    print $port->{data1};
}

Comments

4

You can store a reference to a hash in an array:

push @switchport, \%port; # stores a reference to your existing hash

or

push @switchport, { %port }; # clones the hash so it can be updated separately

Then iterate with, say,

foreach my $port (@switchport) {
    print $port->{'data1'}; # or $$port{'data1'}
}

See man perlref.

Comments

4

To simplify for those who use this question to find a general approach - as in the Heading Question. Mysql theme:

my @my_hashes = ();
my @$rows = ... # Initialize. Mysql SELECT query results, for example.

if( @$rows ) {
    foreach $row ( @$rows ) { # Every row to hash, every hash to an array.
        push @my_hashes, { 
            id => $row->{ id }, 
            name => $row->{ name }, 
            value => $row->{ value }, 
        };
    }
}

To loop and print out:

for my $i ( 0 .. $#my_hashes ) {
    print "$my_hashes[$i]{ id }\n ";
    print "$my_hashes[$i]{ name }\n ";
    print "$my_hashes[$i]{ value }\n ";
}

or

for my $i ( 0 .. $#my_hashes ) {
for my $type ( keys %{ $my_hashes[$i] } ) {
     print "$type=$my_hashes[$i]{$type} ";
}

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.