Adding a hash to an array

56,891

Solution 1

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.

Solution 2

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};
}

Solution 3

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.

Solution 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} ";
}

}

Share:
56,891

Related videos on Youtube

Wizzard
Author by

Wizzard

Updated on October 19, 2020

Comments

  • Wizzard
    Wizzard over 3 years

    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)
    

    but if I do print @switch_ports I just see

    data10data20changed0
    

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

    I think i am going retarded from smashed my head into the desk so hard.

    1 - Can you store a hash in an array?

    2 - Can you have an array of hashes?

    Trying to get...

    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.

    Yes, I fail at Perl

  • DVK
    DVK over 12 years
    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 :)
  • David W.
    David W. over 12 years
    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.