Perl: Is it possible to iterate over an anonymous hash? - perl

It's easy enough to iterate over a list:
foreach my $elem ( 1, 2, 3, 4, 5 ) {
say $elem;
}
or an anonymous array:
foreach my $elem (#{[ 1, 2, 3, 4, 5 ]}) {
say $elem;
}
But is it possible to do the same for an anonymous hash? I tried:
while (my ($key, $value) = each (%{{ a => 1, b => 2, c => 3 }})) {
say "$key=$value";
}
but I get an infinite loop.

A foreach loop evaluates its expression once. while, on the other hand, evaluates its expression once each pass. That means you are repeatedly creating a new hash and grabbing its first element.
You could do the following:
use List::Util 1.29 qw( pairs );
for my $pair ( pairs %{ { a => 1, b => 2, c => 3 } } ) {
my ( $key, $val ) = #$pair;
...
}
But just like your second snippet is a needlessly wasteful and complex version of your first snippet, the above is a needlessly wasteful and complex version of the following:
use List::Util 1.29 qw( pairs );
for my $pair ( pairs a => 1, b => 2, c => 3 ) {
my ( $key, $val ) = #$pair;
...
}

Related

What does it mean to have a perl hash{}{}

My professor has some syntax on a slide that I do not understand.
In perl there is:
$hash{$string}{$anotherString}++;
What does this syntax mean? If it were:
$hash{$string}{$int}++;
Would it be increment the value?
When I print using
while( my( $key, $value ) = each %hash ){print "$key: $value\n";}
My output is
"key": HASH(0xbe0200)
That is a two-dimensional hash, a hash of hashes. It is easy to keep track of structures in Perl once you realize that any single value is in fact a scalar. In the case of multidimensional structures, the scalar value is a reference. For example:
my %outer = ( "foo" => { "bar" => 1 } );
The inner part { "bar" => 1 } is a hash reference. The use of { } in assignment denotes an anonymous hash. This is similar to:
my %inner = ( "bar" => 1 );
my %outer = ( "foo" => \%inner );
Now when you want to reference a value in %inner, you use the first key to access the hash reference, and the second key to access the value in %inner:
print $outer{"foo"}{"bar"}; # prints 1
And when you use the increment operator ++ on a value, it is incremented:
$outer{"foo"}{"bar"}++; # the value is now 2
$hash{string1}{string2}
is a shorter equivalent of
$hash{string1}->{string2}
i.e. it returns a value from a hash of hashes.
By applying the ++ operator, the value in the inner hash is incremented.
My output is "key": HASH(0xbe0200)
That strange output means that what you are trying to print is actually a hash reference:
use strict;
use warnings;
use 5.016; #allows you to use say(), which is equivalent to print()
#plus a newline at the end
my $href = {
a => 1,
b => 2,
};
say $href;
--output:--
HASH(0x100826698)
Or,
my %hash = (
a => 1,
b => 2,
);
say \%hash;
--output:--
HASH(0x1008270a0)
The \ operator gets the reference for the thing on its right hand side.
The easiest way to print the actual hash is using Data::Dumper, which is something you can and will use all the time:
use strict;
use warnings;
use 5.016;
use Data::Dumper;
my $href = {
a => 1,
b => 2,
};
say Dumper($href);
$VAR1 = {
'a' => 1,
'b' => 2
};
Like use warnings;, I consider use Data::Dumper; mandatory for every program.
So, when you see strange output, like HASH(0xbe0200), use Data::Dumper on the value:
my %hash = (
a => 1,
b => { hello => 2, goodbye => 3},
);
while( my( $key, $value ) = each %hash ){
say $key;
say Dumper($value);
say '-' x 10;
}
--output:--
a
$VAR1 = 1;
----------
b
$VAR1 = {
'hello' => 2,
'goodbye' => 3
};
----------
Or, alternatively just use Data::Dumper on the whole structure:
my %hash = (
a => 1,
b => { hello => 2, goodbye => 3},
);
say Dumper(\%hash);
--output:--
$VAR1 = {
'a' => 1,
'b' => {
'hello' => 2,
'goodbye' => 3
}
};
Note that Dumper() is used to show the contents of a hash reference(or any other reference), so if your variable is not a reference, e.g. %hash, then you must turn it into a reference using the \ operator, e.g. \%hash.
Now, if you have this hash:
my %hash = (
a => 1,
b => { hello => 2, goodbye => 3},
);
...to retrieve the value corresponding to 'goodbye', you can write:
say $hash{b}{goodbye}; #=>3
$hash{b} returns the hash (reference) { hello => 2, goodbye => 3}, and you can retrieve values from that hash by using the subscripts {hello} or {goodbye}.
Alternatively, you can write this:
my %hash = (
a => 1,
b => { hello => 2, goodbye => 3},
);
my $string = 'b';
my $anotherString = 'goodbye';
say $hash{$string}{$anotherString}; #=>3
And to increment the value 3 in the hash, you can write:
my $result = $hash{$string}{$anotherString}++;
say $result; #=>3
say $hash{$string}{$anotherString}; #=>4
The postfix ++ operator actually increments the value after the current operation, so $result is 3, then the value in the hash is incremented to 4, something like this:
my $temp = $hash{$string}{$anotherString};
$hash{$string}{$anotherString} = $hash{$string}{$anotherString} + 1;
my $result = $temp;
If you want the increment to happen before the current operation, then you can use the prefix ++ operator:
my $result = ++$hash{$string}{$anotherString};
say $result; #=>4
say $hash{$string}{$anotherString}; #=>4
Finally, if the value at $hash{$string}{$anotherString} is not a number, e.g. 'green', you will get something strange:
my %hash = (
a => 1,
b => { hello => 2, goodbye => 'green'},
);
my $string = 'b';
my $anotherString = 'goodbye';
my $result = $hash{$string}{$anotherString}++;
say $hash{$string}{$anotherString};
--output:--
greeo
perl has a notion that the string that comes after the string 'green' is the string 'greeo' because the letter 'o' comes after the letter 'n' in the alphabet. And if the string you incremented were 'greez' the output would be:
greez original
grefa output
The next letter after 'z' is to start over with 'a', but just like when you increment 9 by 1 and get 10, the increment for 'z' carries over to the column on the left, incrementing that letter by 1, producing the 'f'. Ha!

Sorting and printing a hash of hashes in Perl

Using Perl, I have a HoH similar to this:
%HoH = (
'A' => {
'a' => 4,
'b' => 18,
'c' => 2
},
'B' => {
'a' => 1,
'b' => 2
},
'C' => {
'a' => 1
},
'D' => {
'a' => 1,
'b' => 2,
'c' => 5,
'd' => 9
},
#........ on and on and on .....
);
For each of the capital keys, I want to print the one lower-case key that has the largest value associated with it.
example output:
b,b,a,d...
Any direction at this point would be appreciated, new to the game.
use List::Util qw(reduce);
for my $k1 (sort keys %HoH) {
my $h = $HoH{$k1};
my $k2 = reduce { $h->{$a} > $h->{$b} ?$a :$b } keys %$h;
print "$k1, $k2\n";
}
For example:
for my $k (sort keys %HoH) {
my $h = $HoH{$k};
my $g= (sort {$h->{$b} <=> $h->{$a}} keys %$h)[0];
print "$k: $g \n";
}
(Your original output does not much sense, because the order of the keys of %HoH is not fixed)
Using List::Util's reduce;
use List::Util qw(reduce);
use strict;
use warnings;
my %HoH = ...
for my $k (sort keys %HoH) {
my $h = $HoH{$k};
my $maxKey = reduce {$h->{$a} > $h->{$b} ? $a : $b} keys %$h;
print "$k -> $maxKey\n";
}

Create a hash in Perl

I have a beginner question:
I have an #key_table and many #values_tables.
I want to create a #table of references to hashes, so there is one table, each element points to hash with keys&values from those 2 tables presented at the beginning.
Could anyone help me?
For example:
#keys = (Kate, Peter, John);
#value1 = (1, 2, 3);
#value2 = (a, b, c);
and I want a two-element table that point to:
%hash1 = (Kate=>1, Peter=>2, John=>3);
%hash2 = (Kate=>a, Peter=>b, John=>c);
If you just want to create two hashes, it's really easy:
my ( %hash1, %hash2 );
#hash1{ #keys } = #value1;
#hash2{ #keys } = #value2;
This takes advantage of hash slices.
However, it's usually a mistake to make a bunch of new variables with numbers stuck on the end. If you want this information all together in one structure, you can create nested hashes with references.
Using hash slice is most common way to populate hash with keys/values,
#hash1{#keys} = #value1;
#hash2{#keys} = #value2;
but it could be done in other (less efficient) way using ie. map,
my %hash1 = map { $keys[$_] => $value1[$_] } 0 .. $#keys;
my %hash2 = map { $keys[$_] => $value2[$_] } 0 .. $#keys;
or even foreach
$hash1{ $keys[$_] } = $value1[$_] for 0 .. $#keys;
$hash2{ $keys[$_] } = $value2[$_] for 0 .. $#keys;
This is an example:
use strict;
use warnings;
use Data::Dump;
#Example data
my #key_table = qw/Kate Peter John/;
my #values_tables = (
[qw/1 2 3/],
[qw/a b c/]
);
my #table;
for my $vt(#values_tables) {
my %temph;
#temph{ #key_table } = #$vt;
push #table, \%temph;
}
dd(#table);
#<--- prints:
#(
# { John => 3, Kate => 1, Peter => 2 },
# { John => "c", Kate => "a", Peter => "b" },
#)
This will do it:
use Data::Dumper;
use strict;
my #keys = ("Kate", "Peter", "John");
my #value1 = (1, 2, 3);
my #value2 = ("a", "b", "c");
my (%hash1,%hash2);
for my $i (0 .. $#keys){
$hash1{$keys[$i]}=$value1[$i];
$hash2{$keys[$i]}=$value2[$i];
}
print Dumper(\%hash1);
print Dumper(\%hash2);
This is the output:
$VAR1 = {
'John' => 3,
'Kate' => 1,
'Peter' => 2
};
$VAR1 = {
'John' => 'c',
'Kate' => 'a',
'Peter' => 'b'
};

sum hash of hash values using perl

I have a Perl script that parses an Excel file and does the following : It counts for each value in column A, the number of elements it has in column B, the script looks like this :
use strict;
use warnings;
use Spreadsheet::XLSX;
use Data::Dumper;
use List::Util qw( sum );
my $col1 = 0;
my %hash;
my $excel = Spreadsheet::XLSX->new('inout_chartdata_ronald.xlsx');
my $sheet = ${ $excel->{Worksheet} }[0];
$sheet->{MaxRow} ||= $sheet->{MinRow};
my $count = 0;
# Iterate through each row
foreach my $row ( $sheet->{MinRow}+1 .. $sheet->{MaxRow} ) {
# The cell in column 1
my $cell = $sheet->{Cells}[$row][$col1];
if ($cell) {
# The adjacent cell in column 2
my $adjacentCell = $sheet->{Cells}[$row][ $col1 + 1 ];
# Use a hash of hashes
$hash{ $cell->{Val} }{ $adjacentCell->{Val} }++;
}
}
print "\n", Dumper \%hash;
The output looks like this :
$VAR1 = {
'13' => {
'klm' => 1,
'hij' => 2,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
};
This works great, my question is : How can I access the elements of this output $VAR1 in order to do : for value 13, klm + hij = 3 and get a final output like this :
$VAR1 = {
'13' => {
'somename' => 3,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
};
So basically what I want to do is loop through my final hash of hashes and access its specific elements based on a unique key and finally do their sum.
Any help would be appreciated.
Thanks
I used #do_sum to indicate what changes you want to make. The new key is hardcoded in the script. Note that the new key is not created if no key exists in the subhash (the $found flag).
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my %hash = (
'13' => {
'klm' => 1,
'hij' => 2,
'lkm' => 4,
},
'12' => {
'abc' => 2,
'efg' => 2
}
);
my #do_sum = qw(klm hij);
for my $num (keys %hash) {
my $found;
my $sum = 0;
for my $key (#do_sum) {
next unless exists $hash{$num}{$key};
$sum += $hash{$num}{$key};
delete $hash{$num}{$key};
$found = 1;
}
$hash{$num}{somename} = $sum if $found;
}
print Dumper \%hash;
It sounds like you need to learn about Perl References, and maybe Perl Objects which are just a nice way to deal with references.
As you know, Perl has three basic data-structures:
Scalars ($foo)
Arrays (#foo)
Hashes (%foo)
The problem is that these data structures can only contain scalar data. That is, each element in an array can hold a single value or each key in a hash can hold a single value.
In your case %hash is a Hash where each entry in the hash references another hash. For example:
Your %hash has an entry in it with a key of 13. This doesn't contain a scalar value, but a references to another hash with three keys in it: klm, hij, and lkm. YOu can reference this via this syntax:
${ hash{13} }{klm} = 1
${ hash{13} }{hij} = 2
${ hash{13} }{lkm} = 4
The curly braces may or may not be necessary. However, %{ hash{13} } references that hash contained in $hash{13}, so I can now reference the keys of that hash. You can imagine this getting more complex as you talk about hashes of hashes of arrays of hashes of arrays. Fortunately, Perl includes an easier syntax:
$hash{13}->{klm} = 1
%hash{13}->{hij} = 2
%hash{13}->{lkm} = 4
Read up about hashes and how to manipulate them. After you get comfortable with this, you can start working on learning about Object Oriented Perl which handles references in a safer manner.

Perl map - need to map an array into a hash as arrayelement->array_index

I have a array like this:
my #arr = ("Field3","Field1","Field2","Field5","Field4");
Now i use map like below , where /DOSOMETHING/ is the answer am seeking.
my %hash = map {$_ => **/DOSOMETHING/** } #arr
Now I require the hash to look like below:
Field3 => 0
Field1 => 1
Field2 => 2
Field5 => 3
Field4 => 4
Any help?
%hash = map { $arr[$_] => $_ } 0..$#arr;
print Dumper(\%hash)
$VAR1 = {
'Field4' => 4,
'Field2' => 2,
'Field5' => 3,
'Field1' => 1,
'Field3' => 0
};
my %hash;
#hash{#arr} = 0..$#arr;
In Perl 5.12 and later you can use each on an array to iterate over its index/value pairs:
use 5.012;
my %hash;
while(my ($index, $value) = each #arr) {
$hash{$value} = $index;
}
Here's one more way I can think of to accomplish this:
sub get_bumper {
my $i = 0;
sub { $i++ };
}
my $bump = get_bumper; # $bump is a closure with its very own counter
map { $_ => $bump->(); } #arr;
As with many things that you can do in Perl: Don't do this. :) If the sequence of values you need to assign is more complex (e.g. 0, 1, 4, 9, 16... or a sequence of random numbers, or numbers read from a pipe), it's easy to adapt this approach to it, but it's generally even easier to just use unbeli's approach. The only advantage of this method is that it gives you a nice clean way to provide and consume arbitrary lazy sequences of numbers: a function that needs a caller-specified sequence of numbers can just take a coderef as a parameter, and call it repeatedly to get the numbers.
A very old question, but i had the same problem and this is my solution:
use feature ':5.10';
my #arr = ("Field3","Field1","Field2","Field5","Field4");
my %hash = map {state $i = 0; $_ => $i++} #arr;