Perl reference not printing the expected value - perl

This is my program but why not it is printing my array values instead.
use strict;
use warnings;
use Data::Dumper;
my (#arr1,#arr2) = ([1,1,1,2,3,4],[5,5,5,6,9,87]);
my #arr3 = [\#arr1,\#arr2];
foreach (#arr3){
foreach (#$_){
print $_;
}
}
Output:
ARRAY(0x556414c6b908)ARRAY(0x556414c6b7e8)
but why not it is printing my array values instead.

Because the values are array references. To print the inner values, use dereference:
print #{ $array_ref };
For complex structures (arrays of arrays), you can use Data::Dumper:
use Data::Dumper;
print Dumper($array_ref);
But it still wouldn't work. You can't assign to several arrays at once. The first array gets all the values, the remaining arrays stay empty.
Documented in perlsub:
Do not, however, be tempted to do this:
(#a, #b) = upcase(#list1, #list2);
Like the flattened incoming parameter list, the return list is also
flattened on return. So all you have managed to do here is stored
everything in #a and made #b empty.

First of all, you weren't assigning anything to #arr2. You used something like the following to try to assign to #arr2:
(#arr1, #arr2) = ...;
However, Perl has no way to know how many scalars to assign to #arr1 and how many to assign to #arr2, so it assigns them all to #arr1. Use two different assignments instead.
Secondly, [ ] creates an array and returns a reference to it, so
my #arr1 = [1,1,1,2,3,4];
assigns a single scalar (a reference) to #arr1. This is what you are printing. You want
my #arr1 = (1,1,1,2,3,4);
Same goes for #arr2 and #arr3.
Therefore, your code should be
use strict;
use warnings;
use feature qw( say );
my #arr1 = (1,1,1,2,3,4);
my #arr2 = (5,5,5,6,9,87);
my #arr3 = (\#arr1,\#arr2);
for (#arr3) {
say join ", ", #$_;
}
or
use strict;
use warnings;
use feature qw( say );
my #arr3 = ([1,1,1,2,3,4],[5,5,5,6,9,87]);
for (#arr3) {
say join ", ", #$_;
}

Related

Push operation not working while pushing array to a hash

I have two arrays #arr1 and #arr2 and I have a hash %hash in my Perl code.
I have certain elements in #arr1 and similarly certain elements in #arr2. I want to push elements of #arr1 as key for hash %hash and elements of the array #arr2 as values for those keys for hash %hash.
I am using the below code in my Perl script to do so:
use strict;
use warnings;
use Data::Dumper;
my %hash = ();
my #abc = ('1234', '2345', '3456', '4567');
my #xyz = ('1234.txt', '2345.txt', '3456.txt', '4567.txt');
push(#{$hash{#abc}}, #xyz);
print Dumper(%hash);
After executing, I am getting the below output:
./test.pl
$VAR1 = '4';
$VAR2 = [
'1234.txt',
'2345.txt',
'3456.txt',
'4567.txt'
];
It is not printing the key values but their total number. I need the output to be that every key with its value gets printed after I execute the script.
Can someone please help. Thanks.
You're looking for a slice to assign multiple elements of a hash at once:
use strict;
use warnings;
use Data::Dumper;
my %hash = ();
my #abc = ('1234', '2345', '3456', '4567');
my #xyz = ('1234.txt', '2345.txt', '3456.txt', '4567.txt');
#hash{#abc} = #xyz;
print Dumper(\%hash);
produces (Though order of keys might vary):
$VAR1 = {
'4567' => '4567.txt',
'2345' => '2345.txt',
'3456' => '3456.txt',
'1234' => '1234.txt'
};
Explanation
What push(#{$hash{#abc}}, #xyz); does is push elements into an array reference stored in a single hash entry - #abc is used in scalar context here, which evaluates to the length of the array, hence the 4. Using a slice instead assigns a list of values to a corresponding list of keys.
Then print Dumper(%hash); first turns %hash into a list of alternating keys and values, hence them being two different Data::Dumper entries. Passing a reference to the hash instead makes it print out the actual data structure as one thing.

perl: Use map and foreach at once?

I was wondering if it is possible to make a hash assigning its keys and values at once. Or in general use map and for at one line:
#!/usr/bin/perl
%h = map {$_, $i} qw[a b c] for $i (1..3)
But unfortunatelly not => Number found where operator expected, meant number in the parenthesis. So my question is why am I not able to make double loop by this way? And how otherwise would someone assign hash keys to values (and I dont concern something like $h = {a=>1,b=>2,c=>3} but rather assigning %h = (#keys = #values) ... in other words, how to assign hash by:
2 arrays only (#keys,#values), no scalars
At once (at one line - without block)
Is it even possible in perl?
Populating a hash is simply a matter of assigning a list with alternating keys and values, so you just have to construct the list using the two arrays in an alternating fashion.
use strict;
use warnings;
my #keys = qw(a b c);
my #values = 1..3;
my %h = map { ($keys[$_], $values[$_]) } 0..$#keys;
List::UtilsBy provides a useful abstraction for this in zip_by.
use List::UtilsBy 'zip_by';
my %h = zip_by { #_ } \#keys, \#values;
But actually it's even easier to use slice assignment. Though you technically can't do this in the same statement as the declaration, it's by far the neatest option:
my %h;
#h{#keys} = #values;
Use List::MoreUtils 'zip' or add your own since that module is not a core module:
sub zip(\##){map{($_[0][$_-1],$_[$_])}1..#{$_[0]}}
my %h = zip #keys, #values;
Well, the question is not very clear on 'why?' -- same can be achieved with following code
use strict;
use warnings;
use Data::Dumper;
my $debug = 1;
my %h;
#h{qw(a b c)} = (1..3);
print Dumper(\%h) if $debug;

How do I split "a=1,b=2" into a hash in perl?

I want to do this:
my %options = makeHash("user=bob,pass=123");
Bonus points if anyone can make this work...
my %options = makeHash('user="bob,a",pass=123');
I can easily write the first method with multiple split()'s but I want to know if there is a cool elegant way specific to Perl this can be done...
You can use Text::ParseWords (a core module in Perl 5) parse the fields, and also overcome quoted comma signs inside the fields. Note that the return value is a hash reference, not a hash.
use strict;
use warnings;
use Text::ParseWords;
my $options = makeHash('user="bob,a",pass=123');
sub makeHash {
my $str = shift;
my #foo = quotewords(',', 0, $str); # split into pairs
my %hash = quotewords('=', 0, #foo); # split into key + value
return \%hash;
}
If your keys and values are all alphanumeric then you can just write
my %options = "user=bob,pass=123" =~ /\w+/g;
or, for your second case
my %options = 'user="bob,a",pass=123' =~ /(\w+)="?([\w,]+)/g;
You need to be clear exactly what characters can appear in your data, whether or not there may be spaces around the = etc.

Dereferencing a list reference in hash element

Can someone finish this for me and explain what you did?
my %hash;
#$hash{list_ref}=[1,2,3];
#my #array=#{$hash{list_ref}};
$hash{list_ref}=\[1,2,3];
my #array=???
print "Two is $array[1]";
#array = #{${$hash{list_ref}}};
(1,2,3) is a list.
[1,2,3] is a reference to a list an array (technically, there's no such thing in Perl as a reference to a list).
\[1,2,3] is a reference to a reference to an array.
$hash{list_ref} is a reference to a reference to an array.
${$hash{list_ref}} is a reference to an array.
#{${$hash{list_ref}}} is an array.
Since a reference is considered a scalar, a reference to a reference is a scalar reference, and the scalar dereferencing operator ${...} is used in the middle step.
Others have pretty much already answered the question, but more generally, if you are ever confused about a data structure, use Data::Dumper. This will print out the structure of the mysterious blob of data, and help you parse it.
use strict; #Always, always, always
use warnings; #Always, always, always
use feature qw(say); #Nicer than 'print'
use Data::Dumper; #Calling in the big guns!
my $data_something = \[1,2,3];
say Dumper $data_something;
say Dumper ${ $data_something };
Let's see what it prints out...
$ test.pl
$VAR1 = \[
1,
2,
3
];
$VAR1 = [
1,
2,
3
];
From the first dump, it appears that $data_something is a plain scalar reference to an array reference. That lead me to add the second Dumper after I ran the program the first time. That showed me that ${ $data_something } is now a reference to an array.
I can now access that array like this:
use strict; #Always, always, always
use warnings; #Always, always, always
use feature qw(say); #Nicer than 'print'
use Data::Dumper; #Calling in the big guns!
my $data_something = \[1,2,3];
# Double dereference
my #array = #{ ${ $data_something } }; #Could be written as #$$data_something
for my $element (#array) {
say "Element is $element";
}
And now...
$ test.pl
Element is 1
Element is 2
Element is 3
It looks like you meant:
my $hash{list_ref} = [1,2,3];
and not:
$hash{list_ref} = \[1,2,3];
That latter one got you an scalar reference of a array reference which really doesn't do you all that much good except add confusion to the situation.
Then, all you had to do to refer to a particular element is $hash{list_ref}->[0]. This is just a shortcut for ${ $hash{list_ref} }[0]. It's easier to read and understand.
use strict;
use warnings;
use feature qw(say);
my %hash;
$hash{list_ref} = [1, 2, 3];
foreach my $element (0..2) {
say "Element is " . $hash{list_ref}->[$element];
}
And...
$ test.pl
Element is 1
Element is 2
Element is 3
So, next time you are confused about what a particular data structure looks like (and it happens to the best of us. Well... not the best of us, It happens to me), use Data::Dumper.
my %hash;
#$hash{list_ref}=[1,2,3];
#Putting the list in square brackets makes it a reference so you don't need '\'
$hash{list_ref}=[1,2,3];
#If you want to use a '\' to make a reference it is done like this:
# $something = \(1,2,3); # A reference to a list
#
# or (as I did above)...
#
# $something = [1,2,3]; # Returns a list reference
#
# They are the same (for all intent and purpose)
print "Two is $hash{list_ref}->[1]\n";
# To make it completely referenced do this:
#
# $hash = {};
# $hash->{list_ref} = [1,2,3];
#
# print $hash->{list_ref}[1] . "\n";
To get at the array (as an array or list) do this:
my #array = #{ $hash{list_ref} }
[ EXPR ]
creates an anonymous array, assigns the value returned by EXPR to it, and returns a reference to it. That means it's virtually the same as
do { my #anon = ( EXPR ); \#anon }
That means that
\[ EXPR ]
is virtually the same as
do { my #anon = ( EXPR ); \\#anon }
It's not something one normally sees.
Put differently,
1,2,3 returns a list of three elements (in list context).
(1,2,3) same as previous. Parens simply affect precedence.
[1,2,3] returns a reference to an array containing three elements.
\[1,2,3] returns a reference to a reference to an array containing three elements.
In practice:
my #data = (1,2,3);
print #data;
my $data = [1,2,3]; $hash{list_ref} = [1,2,3];
print #{ $data }; print #{ $hash{list_ref} };
my $data = \[1,2,3]; $hash{list_ref} = \[1,2,3];
print #{ ${ $data } }; print #{ ${ $hash{list_ref} } };

Put an array into a hash of hashes, transforming it into a Hash itself

my %hash = #array;
transforms an array into a hash, but how can do the same with $hash{something}?
$hash{something} = { #array };
The {} around it creates a hashref.
When you write:
my %hash = #array;
What Perl sees is:
my %hash = ($array[0], $array[1], ... $array[$#array]);
So the #array is expanded into a list, and that list is assigned to the plural %hash. The list must have an even number of elements, or you will get a warning (assuming you are using use warnings; in your script, which you always should. use strict; as well).
Broken down even more, it is:
my %hash;
$hash{$array[0]} = $array[1];
$hash{$array[2]} = $array[3];
...
$hash{$array[$#array - 1]} = $array[$#array];
So with the translation from #array to %hash explained, to insert this hash into a hash of hashes at a particular key:
$HoH{key} = \%hash;
Where the \ character takes a reference to %hash. If %hash is not needed elsewhere you could constrain it with a block:
{
my %hash = #array;
$HoH{key} = \%hash;
}
Using a do {...} makes it a little shorter:
$HoH{key} = do {my %hash = #array; \%hash};
But not much shorter. If the above seems tedious to you, it should. In Perl, the above construct can be reduced to:
$HoH{key} = { #array };
Where the {...} denotes the anonymous hash reference constructor, equivalent to
do {my %hash = (...); \%hash}
In addition to {...}, Perl provides the [...] construct to create anonymous array references. The ... of both constructs sees list context.