Perl: Return hash from subroutine - arrays

I have been trying examples for hours but I can't seem to grasp how to do what I want to do.
I want to return a hash from a subroutine, and I figured a reference was the best option. Here's where it gets a bit tricky. I want to reference a hash like $hash{$x}. I am still a noob at perl :/
1.First question, the examples I use seem to show it is ok to use $hashTable{$login}, should I be using %hashTable{$login} or does it not matter? Below is the code:
sub authUser {
$LocalPath = "/root/UserData";
open(DATAFILE, "< $LocalPath");
while( $linebuf = <DATAFILE> ) {
chomp($linebuf);
my #arr = split(/:/, $linebuf);
my $login = $arr[1]; # arr[1] contains the user login names
my $hashTable{ $login } = "$arr[0]"; #$arr[0] is account number
}
close DATAFILE;
return \$hashTable{ $login };
}
I then want to test this data to see if a login is present, here is my test method
# test login Dr. Brule which is present in UserData
my $test = "Dr. Brule";
my $authHash = &authUser();
if ( $authHash{ $test } ) {
print "Match for user $test";
}
else {
print "No Match for user $test";
}
2.Should my $authHash be really $authHash{ $something }, I am so confused on this
Edit: After some reading tips, still attempting but no dice, any help would be greatly appreciated
Edit 2: Can anyone modify my code so that I can understand the answers better? I'm sorry I can't seem to get this to work at all, I have been trying for hours and I really want to know the correct way to do this, I can post my various tries but I feel that will be a waste of real estate.

First off, as mpapec mentioned in comments, use strict; use warnings;. That will catch most common mistakes, including flagging most of the problems you're asking about here (and usually providing hints about what you should do instead).
Now to answer questions 1 and 2:
%hash is the hash as a whole. The complete data structure.
$hash{key} is a single element within the hash.
Therefore, \%hash is a reference to %hash, i.e., the whole hash, which appears to be what you intend to return in this case. \$hash{key} is a reference to a single element.
Where it gets tricky in your second question is that references are always scalars, regardless of what they refer to.
$hash_ref = \%hash
To get an element out of a hash that you have a reference to, you need to dereference it first. This is usually done with the -> operator, like so:
$hash_ref->{key}
Note that you use -> when you start from a reference ($hash_ref->{key}), but not when you start from an actual hash ($hash{key}).
(As a side note on question 2, don't prefix sub calls with & - just use authUser() instead of &authUser(). The & is no longer needed in Perl 5+ and has side-effects that you usually don't want, so you shouldn't get in the habit of using it where it's not needed.)
For question 3, if you're only going to check once, you may as well just loop over the array and check each element:
my $valid;
for my $username (#list_of_users) {
if ($login eq $username) {
$valid = 1;
last; # end the loop since we found what we're looking for
}
}
if ($valid) {
print "Found valid username $login\n";
} else {
print "Invalid user! $login does not exist!\n";
}

To be clear, Perl works with scalars or lists of them:
$scalar = 1;
#list = ( $scalar, $scalar, $scalar );
Each item of list may be accessed by index, e.g. $list[1].
You can also access items by name. This structure is called a hash: $hash{ name1 }
%hash = ( 'name1', $scalar, 'name2', $scalar, 'name3', $scalar )
But, as you can see, this is still a list. Notice the "()" around it.
And again, each item of the list can be only a scalar.
I have not seen this in any book, but the $ sign means one value and # means list of values.
In this example you have one value, so you use the $ sign:
$scalar = $hash{ name1 };
$scalar = $list[ 1 ];
In this next example you have a list of values, so you use "#":
#list2 = #list1; # copy all items
#list2 = #list[ 1, 3..5 ]; # copy four items with index 1,3,4,5
#list2 = #hash{ 'name1', 'name3' }; #copy two items with index 'name1', 'name2'
Perl has references. This is powerful tool.
$ref = \$scalar;
$ref = \#list;
$ref = \%hash;
$ref is also scalar, because it has only one value. To access to underlying data referred by this $ref, you should use a dereference.
$scalar = $$ref;
#list = #$ref;
%hash = %$ref;
But actually, you do not want the whole list or hash. You just want some item in them. For this, you use -> and either [] to tell Perl you want to access a list element, or {} to tell Perl you want to access a hash element:
$scalar = $ref->[ 1 ];
$scalar = $ref->{ name1 };
NOTICE: you're accessing one element, so you use the $ sign.
If you want list of elements from array or hashes, you use the # sign. For example:
#list = #$ref[ 1, 3..5 ];
#list = #$ref{ 'name1', 'name2' };
1st: $ref - returns reference to structure. $ says you get one value from variable 'ref'
2nd: #$ref - you dereference $ref. # says that you want to access the list of items by that reference.
3rd-a: you get '1,3,4,5' items from array (NOTICE: [])
3rd-b: you get 'name1', 'name2' items from hash (NOTICE: {})
But when you a get reference to hash or list and put this reference to another hash or array, we may create complex structures such as an array of hashes of hashes, or hash of arrays of hashes. Examples:
#list = ( 1, 2, 3, 4, 5 );
%hash = ( 'a', 1, b => 2 );
#list2 = ( \#list, \%hash, 3, 'y' );
%hash2 = ( name1 => \#list2, d => 4 );
%hash2 = ( 'name1', \#list2, 'd', 4 ); #same. no any difference.
$href = \%hash2;
=> - just quote left operand and put , after it.
If you want to access to one item of 'hash2':
$scalar = $hash2{ name1 };
$scalar = $href->{ name1 };
Using $href-> after dereferencing will mean %hash2.
If you want to access to two or more items of 'hash2':
#list = #hash2{ 'name1', 'd' };
#list = #$href{ 'name1', 'd' };
Using #$href after dereferencing will mean %hash2
IN DETAIL:
$scalar = $hash2{ name1 }; # <--- What does this mean???
$hash2 means we access one item of %hash2. Which is a reference to a list:
$list_ref = $hash2{ name1 };
$scalar = $list_ref->[ 1 ]; # <--- what we get here???
$list_ref means we access one item. ->[ means we access the list. Because $list_ref refers to #list2, we access \%hash. We can complete that in one step:
$scalar = $hash2{ name1 }->[ 1 ];
You may think here as you replace the text '$list_ref' by '$hash2{ name1 }'
We say that [ 1 ] refers to %hash. So to access one item of that hash we use $ again:
$hash_ref = $hash2{ name1 }->[ 1 ];
$scalar = $hash_ref->{ b };
$hash_ref means we access one item. ->{ means we access to hash. Because $hash_ref refers to %hash we access 2. We can complete that in one step:
$scalar = $hash2{ name1 }->[ 1 ]->{ b };
You again may think here as you replace text '$hash_ref' by '$hash2{ name1 }->[ 1 ]'. But hash2 here is %hash2. What about $href? Please remember this example:
$scalar = $hash2{ name1 };
$scalar = $href->{ name1 };
You may notice, if you access the item by ref you just add ->. Compare:
#l = ( 1, 2, 3, 4 );
$scalar = $l[ 1 ]; # to access to second item of #l list
$hr = \#l;
$scalar = $hl->[ 1 ]; # to access to second item of #l list
%h = #l;
$scalar = $h{ 1 };
$hr = \%h;
$scalar = $hr->{ 1 };
The type of bracket after -> will be [ for an array or { for a hash item.
What about $href?
$scalar = $hash2{ name1 }->[ 1 ]->{ b };
$scalar = $href->{ name1 }->[ 1 ]->{ b };
After the first dereference we do not require ->
$scalar = $hash2{ name1 }[ 1 ]{ b };
^-- first dereference
$scalar = $href->{ name1 }[ 1 ]{ b };
^--first dereference
Returning to your question: in Perl, you may pass a LIST of values to subs and return a LIST also.
sub test {
return #_;
}
Here we return all items we get.
return \%hash; # fn()->{ name1 }; # actually all these is list of one item
return \#list; # fn()->[ 1 ]; # so we may write: (fn())[0]->[ 1 ];
return $scalar; # fn(); # here also list of one item
return ( $scalar, \%hash, \#list );
(fn())[ 0 ];
(fn())[ 1 ]->{ name1 };
(fn())[ 2 ]->[ 1 ];
it is ok to use $hashTable{$login}, should I be using %hashTable{$login} or does it not matter?
Nope. You should use $ to access one item from %hashTable. $hashTable{$login} is right.
If you want to extract two logins you should use #:
#list = #hashTable{ 'login1', 'login2' };
# or
$l1 = 'login1';
$l2 = 'login2';
#list = #hashTable{ $l1, $l2 };
return \$hashTable{ $login };
Wrong. You return one item from a hash. So return $hashTable{ $login } is right.
2.Should my $authHash be really $authHash{ $something }, I am so confused on this
I suppose your %hashTable is a list of hashes keyed by $login. Like this:
$login1 = { name => 'Vasiliy', pass => 'secret' } # ref to hash
%login2 = ( name => 'Petrovich', pass => '^&UDHJ' ); # just a hash
%hashTable = (
vasya => $login1, # items are always refs!!!
piter => \%login2, # items are always refs!!!
)
So authUser sub will return a reference:
my $authHash = authUser( 'vasya' ); # & is not required at all
Because of $authHash, if the reference is to a hash you should use ->
if( $authHash->{ pass } eq $password ) {
...
}
But if your authUser is a parse config file and returns all users, you should to rename it to loadUsers and return a reference to the hash:
sub loadUsers {
....
return \%hashTable;
}
my $usersDB = loadUsers;
if( $usersDB->{ $login }->{ pass } eq $password ) {
print 'You have granged access';
}
else { ... }
Edit 2: Can anyone modify my code so that I can understand the answers better?
Nope. Read my tutorial. To understand how to write code you should do it yourself.
AS ADVICE
While you're a newbie:
always use hashref and listref.
always use -> to access items.
always use $ sigil as first char.
.
$list = [ 1, 2, 3 ];
$hash = { a => 1, b => 2 };
$list->[ 2 ];
$hash->{ b };
Exceptions will be when you access the whole array or hash:
#l = #$list;
%h = %$hash;
#l = keys %$hash;
#l = values %$hash;

You probably don't want to be doing this:
return \$hashTable{ $login };
You are returning a reference (pointer) to your hash.
Try just
return $hashTable{$login}
Which will return the account number.
Or if you really want a hash with a bunch of people in there then
return \$hashTable
is fine (don't add the {$login} part), but on the other side you need to be
dereferencing.
eg:
my $p = authUser()
if ($p->{'Dr. Brule'})
...
Notice the -> in there. It de-references the pointer you passed back.

Related

Compare two 2D array to match the string

I want to compare string in two 2D array but the size are not the same. So, I want to shift the element in one of the array to match with all of the element in another array but i run out of idea on how the looping should be.
This is my first time using perl language. I learned c language before.
#!/usr/intel/pkgs/perl/5.14.1/bin/perl
use Data::Dumper qw(Dumper);
#clk = (
'prescc_ux_aux_clk',
'prescc_ux_prim_clk',
'usb2_phy_side_clk',
'usb3_phy_side_clk',
'ux_prim_clk',
'ux_side_clk',
'ux_xtal_frm_refclk',
'uxd_aux_clk',
'uxd_pgcb_clk',
'uxd_prescc_aux_clk',
'uxd_prim_clk',
'uxd_side_clk',
'uxd_suspend_clk');
#clkack = (
'ccu_ux_xtal_frm_refclk_ack',
'ibbs_ux_prim_clkack',
'sbr_ux_side_clkack',
'uxd_aux_clkack',
'uxd_pgcb_clkack',
'uxd_prim_clkack',
'uxd_side_clkack');
foreach(#clk){
#clkline = map {[split /_/,$_]} #clk;
}
foreach(#clkack){
#clkackline = map{[split /_/,$_]} #clkack;
}
#print Dumper #clkline;
$match = 0;
$clkack_row = #clkackline; #no. of row in clkackline
$clk_row = #clkline;
for ($i=0; $i<$clkack_row; $i++){
$clkackcolumn = #{$clkackline[$i]};
for ($j=0; $j<$clkackcolumn; $j++){
for ($m=0; $m<$clk_row; $m++){
$clkcolumn = #{$clkline[$m]};
for ($n=0; $n<$clkcolumn; $n++){
if ($clkline[$i][$j] eq $clkacline[$m][$n]){
$match = $match + 1;
print "$match\n";
}
}
}
}
}
I expect it to loop the #clkackline array and compare it with the #clkline array. If it's matching then it will give how many match it have, hence the $match variable.
Edited:
I need to split it by '_' so that i can get the element divided by only the word inside another array.
Eg:
$clk[0] = ux_prim_clk will result in;
$clkline[0][0] = ux, $clkline[0][1] = prim, $clkline[0][2] = clk.
Then i need to compare with the #clkackline array element by element but in sequential order.
Eg:
$clkline[0][0] = $clkackline[0][0],
$clkline[0][1] = $clkackline[0][1],
$clkline[0][2] = $clkackline[0][2].
But another problem is i need to compare #clkline with all of the element in clkackline. But since the size will be the constraint, then i need to shift the #clkackline to match with #clkline or vice versa.
Eg:
First check:
$clkline[1][0] = $clkackline[0][0],
$clkline[1][1] = $clkackline[0][1],
$clkline[1][2] = $clkackline[0][2].
Second check:
$clkline[1][0] = $clkackline[0][1],
$clkline[1][1] = $clkackline[0][2],
$clkline[1][2] = $clkackline[0][3].
This is just an example of course, but you can see that i need it to delete the first column in $clkackline[0].
Then i need to calculate the percentage of how much it will match.
Eg:
ux_prim_clk vs uxd_prim_clkack will return 33.33%.
Then store the element with highest match inside another array (eg: #clknew)
I think you may be over complicating your problem, to answer your original question of comparing two lists I have a script that will compare two lists and tell you what doesn't exist in each. If this inst exactly what you need let me know and we can change it up to fit your use. As with just about anything in Perl there is probably a module that will do all this for you.
#!/usr/bin/env perl
use strict;
use warnings;
use List::Util qw(any);
my #list1 = ('prescc_ux_aux_clk',
'prescc_ux_prim_clk',
'usb2_phy_side_clk',
'usb3_phy_side_clk',
'ux_prim_clk',
'ux_side_clk',
'ux_xtal_frm_refclk',
'uxd_aux_clk',
'uxd_pgcb_clk',
'uxd_prescc_aux_clk',
'uxd_prim_clk',
'uxd_side_clk',
'uxd_suspend_clk'
);
my #list2 = ('ccu_ux_xtal_frm_refclk_ack',
'ibbs_ux_prim_clkack',
'sbr_ux_side_clkack',
'uxd_aux_clkack',
'uxd_pgcb_clkack',
'uxd_prim_clkack',
'uxd_side_clkack'
);
print "\n==== LIST 1 TO LIST 2 COMPARISON, Does not exist in list 2 ====\n";
foreach my $first (#list1) {
if ( any { $_ eq $first} #list2) { next; }
else { print $first . "\n"; }
}
print "\n==== LIST 2 TO LIST 1 COMPARISON, Does not exist in list 1 ====\n";
foreach my $first (#list2) {
if ( any {$_ eq $first} #list1) { next; }
else { print $first . "\n"; }
}

How do I store a hash which is inside an array element?

The background
I got a Perl module which utilizes an array for its input/output parameters, like this:
Execute({inputfile => $req->{modules}.'filename', param => \#xchange});
Inside the module a hash is build and returned via reference
$param[0] = \%values;
This is all fine and good (I think) and print Dumper #xchange[0]; will output my desired content as
$VAR1 = { '33' => 'Title1', '53' => 'Title2', '21' => 'Title3' };
The goal
I would like to loop over the content and print the key/value pairs one by one, for example like this
%testhash = ('33' => 'Test1', '53' => 'Test2', '21' => 'Test3' );
foreach $key (keys %testhash) {
print "LOOP: $key, value=$testhash{$key}\n";
}
This loop does work as intended and dumping my testhash via print Dumper \%testhash; outputs the same as the array element above
$VAR1 = { '33' => 'Test1', '53' => 'Test2', '21' => 'Test3' };
The problem
The trouble now seems to be that although both structures appear to be of the same kind I cant get my head arround, how to properly access the returned hash which is stored inside #xchange[0].
I did try %realhash = #xchange[0]; and %realhash = \#xchange[0];, but then print Dumper \%realhash; will output $VAR1 = { 'HASH(0xa7b29c0)' => undef }; or $VAR1 = { 'REF(0xa7833a0)' => undef }; respectively.
So I either need a way to get the content of #xchange[0] inside a clean new hash or a way to foreach loop over the hash inside the #xchange[0] element.
I guess I am getting screwed by the whole hash reference concept, but I am at a loss here and can't think of another way to google for it.
$xchange[0] is a hash reference. Use the dereference operator %{...} to access it as a hash.
%realhash = %{$xchange[0]};
#xchange[0] is a scalar value, it contains the reference to a hash. When you assign it to a hash
%hash = #xchange[0];
The reference is stringified into something like HASH(0xa7b29c0), and you get the warnings
Scalar value #xchange[0] better written as $xchange[0] at ...
Reference found where even-sized list expected at ...
That is to say, you get these warnings, unless you have been so foolish as to not turn warnings on with use warnings.
The first one means what it says. The second one means that the list you assign to a hash should have an even number of elements: one value for every key. You only passed a "key" (something that Perl took as a key). The value then becomes undef, as noted in your Data::Dumper output:
$VAR1 = { 'HASH(0xa7b29c0)' => undef }
What you need to do is dereference the reference.
my $href = $xchange[0];
my %hash = %$href; # using a transition variable
my %hash2 = %{ $xchange[0] } # using support curly braces
perldsc
use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys=1;
my %testhash = ('33' => 'Test1', '53' => 'Test2', '21' => 'Test3' );
# Add hash as first element of xchange AoH
my #xchange = \%testhash;
# Derefererence 1st element of AoH as a hash
my %realhash = %{ $xchange[0] };
# Dump new hash
print Dumper(\%realhash);
__END__
$VAR1 = {
'21' => 'Test3',
'33' => 'Test1',
'53' => 'Test2'
};

perl: trouble passing array to function

So all i want to do is pass a an array to a function (or subroutine) in PERL
So #Temp contains 2 arrays
[0] = {xx,xx,xx,xx,xx}
[1] = {xx,xx,xx,xx,xx}
#returns array containing two arrays
my #temp = $lineParser->parseLine($_);
#handOne = $cardFactory->createHand(#Temp[0]);
#handTwo = $cardFactory->createHand(#Temp[1]);
This is the createHand method wich is contained in a seperate class (or package or whatever)
sub createHand
{
my $self = shift;
my #temp = #_;
my #arrayOfCards;
foreach(#temp)
{
my $value = substr($_,0,1);
my $color = substr($_,1,1);
push(#arrayOfCards,new Card($value,$color));
}
return #arrayOfCards;
}
The problem i am having is that the array gets passed but is contains ARRAY(XXXXX) at the start of the array.
E.g. {0 ARRAY(xxxxxx), 0 'xx', 1 'xx', ...}
Why does this happen?
How can I manage to do this correctly?
If you turn on warnings, you will get the following one:
Scalar value #Temp[0] better written as $Temp[0]
If you want to pass the referenced array by value, you have to dereference it:
#handOne = $cardFactory->createHand( #{ $Temp[0] } );
sub createHand
{
my $self = shift;
my ($temp) = #_;
my #arrayOfCards;
foreach(#$temp)
{
my $value = substr($_,0,1);
my $color = substr($_,1,1);
push(#arrayOfCards,new Card($value,$color));
}
return #arrayOfCards;
}
Also take note that #temp[0] is array slice in case where scalar (array ref) is wanted, so it's better to state right intention:
#handOne = $cardFactory->createHand($temp[0]);
You are passing a reference instead of a value.
my #temp = $lineParser->parseLine($_);
#handOne = $cardFactory->createHand($Temp[0]);
#handTwo = $cardFactory->createHand($Temp[1]);
so in a nutshell change #temp[0] to $temp[0] when passing the argument

How to create objects out of each element in array?

I have a module with a new constructor:
package myClass;
sub new
{
my $class = shift;
my $arrayreference = shift;
bless $arrayreference, $class;
return $arrayreference;
};
I want to do something like:
foreach $ref (#arrayref)
{
$array1 = myClass->new($ref);
}
$array1 is being rewritten each time, but I want each element in the array to have a distinct object name (ex. $array1, $array2, $array3 etc.)
If you are working with a plural data structure (an array), then you need to store the result into a plural container (or multiple scalar containers). The idomatic way to do this is to use the map function:
my #object_array = map {myClass->new($_)} #source_array;
If you know that #source_array contains a fixed number of items, and you want scalars for each object:
my ($foo, $bar, $baz) = map {myClass->new($_)} #source_with_3_items;
I think you should use some hash or array to contain the objects.
foreach $ref (#arrayref)
{
push #array, myClass->new($ref);
$hash{$key++} = myClass->new($ref);
}
thus you can access them with $array[42] or $hash{42}.
There is essentially no name difference between $array[1] and $array1. There is a programmatic difference in that $array[1] can be "pieced together" and, under modern Perl environments $array1 can't. Thus I can write $array[$x] for any valid $x and get an item with a "virtual name" of $array.$x.
my #objects = map { MyClass->new( $_ ); } #data_array;
Thus, if you just want to append a number, you probably just want to collect your objects in an array. However, if you want a more complex naming scheme, one or more levels of hashes is probably a good way to go.
If you had a way to derive the name from the object data once formed, and had a method called name, you could do this:
my %object_map
= map { my $o = MyClass->new( $_ ); ( $o->name => $o ); } #data_array
;
Are you are trying to do it in place?
my #objects = (
{ ...args for 1st object... },
{ ...args for 2nd object... },
...
);
$_ = Class->new($_) for #objects;
However, you should avoid reusing variables like that.
my #object_data = (
{ ...args for 1st object... },
{ ...args for 2nd object... },
...
);
my #objects = map Class->new($_), #object_data;
I agree with Ade YU and Eric Strom, and have +1'd their answers: you should use one of their approaches. But what you ask is technically possible, using symbolic references, so for completeness' sake:
foreach my $i (0 .. $#arrayref)
{
no strict refs;
my $varname = 'array' . ($i + 1);
${$varname} = myClass->new($arrayref[$i]);
}

Adding a hash to an array

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.
Can you store a hash in an array?
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.
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.
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};
}
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.
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} ";
}
}

Resources