How to dereference hash references - perl

UPDATE: Everything I know about referencing/dereferencing came from here: http://www.thegeekstuff.com/2010/06/perl-array-reference-examples/
I'm working with a library that (from the library documentation):
Returns a reference to an array of hash references
This conceptually makes sense to me (i'm not new to programming) but doesn't make sense functionally (i'm, apparently, very new to perl).
Here's some code:
my $Obj = QA::STK::ModuleImUsing->new(arguments, to, new);
$Obj->createClient();
$Obj->sync( ['/tmp/files/...']);
my $result = $Obj->method_in_question(['/tmp/files/ff-latest.xml']);
So far so good. $result now holds the reference to an array.
So when I do this:
print "Result: #{ $result} \n";
I get:
Result: HASH(0x20d95b0)
Lovely! But I still need to dereference the hash. However, here's where things get weird (or maybe they've already gotten weird?).
my $hash_ref = #{ $result};
print Dump($hash_ref));
I get this:
$VAR1 = 1;
Which...isn't what I was expecting at all.
Are my expectations wrong or am I dereferencing things in the wrong way?

If #$result is an array then your LHS must be a list. Otherwise $hashref will be assigned the array size.
my ($hash_ref) = #{ $result};
print Dump($hash_ref));

We have a %hash.
To this, we have a hash reference $hashref = \%hash
This hashref is inside an array #array = ($hashref)
We get an array reference: $arrayref = \#array
This is our result: $result = $arrayref. Not let's work backwards:
#result_array = #$result
$result_hashref = $result_array[0] or combined: $result_hashref = $result->[0]
%result_hash = %$result_hashref or combined: %result_hash = %{ $result->[0] } (note that these make a copy)
To get an element in the result hash, we can do
$result_hash{$key}
$result_hashref->{$key}
$result->[0]{$key}
Note that $scalar = #{ $arrayref } does not work as you expect, because an array (which #{ … } is) returns the length of the array in scalar context. To force list context in an assignment, put the left hand side into parens: (…) = here_is_list_context (where … is a list of zero or more lvalues).

my $hash_ref = #{$result};
is the same as
my #array = #{$result};
my $hash_ref = #array;
This forces the array to be evaluated in scalar context by the assignment operator, which causes it to evaluate as the number of items in the array, or 1.

Related

perl reference and dereference on return statement

I am learning perl, and have a problem related to references.
I am working on the function get_id.
If i return $self->{_id}, I will get two array address which cannot run (c).
In my understanding, $a is the reference and #{$a} is array and #{$a}[0] would return the value 0?
This is my first time post question on stackoverflow, therefore the explaination may not clear enough, sorry about that.
#a1 = [0]
#a2 = [1]
my self{
_id = [\#a1,\#a2]; } //given
sub get_id(){
my $self = shift;
return $self->{_id};
}
sub print{
...
my $a = $obj -> get_id();
my $b = #{$a}[0] * 100; // c (given)
..
}
When $arr is a reference to an array, #$arr or #{ $arr } is the array it refers to. The first (or zeroth) element of the array is $arr->[0] (or $$arr[0], but the arrow notation is more common).
The following
my #arr = [0];
creates an array with a single element, and that element is an array reference. To get the 0, you need to do
$arr[0]->[0]
which can be shortened to
$arr[0][0]
To create an array with 0 as the single element, use
my #arr = (0);
BTW, don't use $a and $b, they are special variables used in sort. Lexicalising them with my can lead to bugs that are hard to debug.

Perl find out if X is an element in an array

I don't know why small things are too not working for me in Perl. I am sorry for that.
I have been trying it around 2 hrs but i couldn't get the results.
my $technologies = 'json.jquery..,php.linux.';
my #techarray = split(',',$technologies);
#my #techarray = [
# 'json.jquery..',
# 'php.linux.'
# ];
my $search_id = 'json.jquery..';
check_val(#techarray, $search_id);
And i am doing a "if" to search the above item in array. but it is not working for me.
sub check_val{
my #techarray = shift;
my $search_id = shift;
if (grep {$_ eq $search_id} #techarray) {
print "It is there \n";
}else{
print "It is not there \n";
}
}
Output: It always going to else condition and returns "It is not there!" :(
Any idea. Am i done with any stupid mistakes?
You are using an anonymous array [ ... ] there, which as a scalar (reference) is then assigned to #techarray, as its only element. It is like #arr = 'a';. An array is defined by ( ... ).
A remedy is to either define an array, my #techarray = ( ... ), or to properly define an arrayref and then dereference when you search
my $rtecharray = [ .... ];
if (grep {$_ eq $search_id} #$rtecharray) {
# ....
}
For all kinds of list manipulations have a look at List::Util and List::MoreUtils.
Updated to changes in the question, as the sub was added
This has something else, which is more instructive.
As you pass an array to a function it is passed as a flat list of its elements. Then in the function the first shift picks up the first element,
and then the second shift picks up the second one.
Then the search is over the array with only 'json.jquery..' element, for 'php.linux.' string.
Instead, you can pass a reference,
check_val(\#techarray, $search_id);
and use it as such in the function.
Note that if you pass the array and get arguments in the function as
my (#array, $search_id) = #_; # WRONG
you are in fact getting all of #_ into #array.
See, for example, this post (passing to function) and this post (returning from function).
In general I'd recommend passing lists by reference.

Why can't I assign a variable to a hash entry in Perl?

Sorry, I'm super rusty with Perl. See the following code:
foreach my $hash (keys %greylist)
{
$t = $greylist{$hash};
print $greylist{$hash}[4] . "\n";
print $t[4] . "\n";
}
Why does $t[4] evaluate to a blank string, yet $greylist{$hash}[4] which should be the same thing evaluates to an IP address?
$greylist{$hash} contains an array reference. When you do:
print $greylist{$hash}[4];
Perl automatically treats it as an array reference but when you do:
$t = $greylist{$hash};
print $t[4];
You're assigning the array reference to a scalar variable, $t, then attempting to access the 5th element of another variable, #t. use strict would give you an error in this scenario.
Use the arrow operator, ->, to dereference:
$t = $greylist{$hash};
print $t->[4];
perlreftut has a note about this:
If $aref holds a reference to an array, then $aref->[3] is the fourth element of the array. Don't confuse this with $aref[3] , which is the fourth element of a totally different array, one deceptively named #aref . $aref and #aref are unrelated the same way that $item and #item are.

Dereferencing in case of $_[0], $_[1] ..... so on

please see the below code:
$scalar = 10;
subroutine(\$scalar);
sub subroutine {
my $subroutine_scalar = ${$_[0]}; #note you need the {} brackets, or this doesn't work!
print "$subroutine_scalar\n";
}
In the code above you can see the comment written "note you need the {} brackets, or this doesn't work!" . Please explain the reason that why we cant use the same statement as:
my $subroutine_scalar = $$_[0];
i.e. without using the curly brackets.
Many people have already given correct answers here. I wanted to add an example I found illuminating. You can read the documentation in perldoc perlref for more information.
Your problem is one of ambiguity, you have two operations $$ and [0] working on the same identifier _, and the result depends on which operation is performed first. We can make it less ambiguous by using the support curly braces ${ ... }. $$_[0] could (for a human anyway) possibly mean:
${$$_}[0] -- dereference the scalar $_, then take its first element.
${$_[0]} -- take element 0 of the array #_ and dereference it.
As you can see, these two cases refer to completely different variables, #_ and $_.
Of course, for Perl it is not ambiguous, we simply get the first option, since dereferencing is performed before key lookup. We need the support curly braces to override this dereferencing, and that is why your example does not "work" without support braces.
You might consider a slightly less confusing functionality for your subroutine. Instead of trying to do two things at once (get the argument and dereference it), you can do it in two stages:
sub foo {
my $n = shift;
print $$n;
}
Here, we take the first argument off #_ with shift, and then dereference it. Clean and simple.
Most often, you will not be using references to scalar variables, however. And in those cases, you can make use of the arrow operator ->
my #array = (1,2,3);
foo(\#array);
sub foo {
my $aref = shift;
print $aref->[0];
}
I find using the arrow operator to be preferable to the $$ syntax.
${ $x }[0] grabs the value of element 0 in the array referenced by $x.
${ $x[0] } grabs the value of scalar referenced by the element 0 of the array #x.
>perl -E"$x=['def']; #x=\'abc'; say ${ $x }[0];"
def
>perl -E"$x=['def']; #x=\'abc'; say ${ $x[0] };"
abc
$$x[0] is short for ${ $x }[0].
>perl -E"$x=['def']; #x=\'abc'; say $$x[0];"
def
my $subroutine_scalar = $$_[0];
is same as
my $subroutine_scalar = $_->[0]; # $_ is array reference
On the other hand,
my $subroutine_scalar = ${$_[0]};
dereferences scalar ref for first element of #_ array, and can be written as
my ($sref) = #_;
my $subroutine_scalar = ${$sref}; # or $$sref for short
Because $$_[0] means ${$_}[0].
Consider these two pieces of code which both print 10:
sub subroutine1 {
my $scalar = 10;
my $ref_scalar = \$scalar;
my #array = ($ref_scalar);
my $subroutine_scalar = ${$array[0]};
print "$subroutine_scalar\n";
}
sub subroutine2 {
my #array = (10);
my $ref_array = \#array;
my $subroutine_scalar = $$ref_array[0];
print "$subroutine_scalar\n";
}
In subroutine1, #array is an array containing the reference of $scalar. So the first step is to get the first element by $array[0], and then deference it.
While in subroutine2, #array is an array containing an scalar 10, and $ref_array is its reference. So the first step is to get the array by $ref_array, and then index the array.

How to manipulate a hash-ref with Perl?

Take a look at this code. After hours of trial and error. I finally got a solution. But have no idea why it works, and to be quite honest, Perl is throwing me for a loop here.
use Data::Diff 'Diff';
use Data::Dumper;
my $out = Diff(\#comparr,\#grabarr);
my #uniq_a;
#temp = ();
my $x = #$out{uniq_a};
foreach my $y (#$x) {
#temp = ();
foreach my $z (#$y) {
push(#temp, $z);
}
push(#uniq_a, [$temp[0], $temp[1], $temp[2], $temp[3]]);
}
Why is it that the only way I can access the elements of the $out array is to pass a hash key into a scalar which has been cast as an array using a for loop? my $x = #$out{uniq_a}; I'm totally confused. I'd really appreciate anyone who can explain what's going on here so I'll know for the future. Thanks in advance.
$out is a hash reference, and you use the dereferencing operator ->{...} to access members of the hash that it refers to, like
$out->{uniq_a}
What you have stumbled on is Perl's hash slice notation, where you use the # sigil in front of the name of a hash to conveniently extract a list of values from that hash. For example:
%foo = ( a => 123, b => 456, c => 789 );
$foo = { a => 123, b => 456, c => 789 };
print #foo{"b","c"}; # 456,789
print #$foo{"c","a"}; # 789,123
Using hash slice notation with a single element inside the braces, as you do, is not the typical usage and gives you the results you want by accident.
The Diff function returns a hash reference. You are accessing the element of this hash that has key uniq_a by extracting a one-element slice of the hash, instead of the correct $out->{uniq_a}. Your code should look like this
my $out = Diff(\#comparr, \#grabarr);
my #uniq_a;
my $uniq_a = $out->{uniq_a};
for my $list (#$uniq_a) {
my #temp = #$list;
push #uniq_a, [ #temp[0..3] ];
}
In the documentation for Data::Diff it states:
The value returned is always a hash reference and the hash will have
one or more of the following hash keys: type, same, diff, diff_a,
diff_b, uniq_a and uniq_b
So $out is a reference and you have to access the values through the mentioned keys.