Size of array inside hash - perl

EDIT: Providing code as per demand from mppac:
I wanted the length of array inside hash.
Why is below showing undef?
$ >cat test.pl
#!/usr/bin/perl
use Data::Dumper;
my %static_data_key = (
'NAME' =>['RAM','SHYAM','RAVI','HARI'],
);
print Dumper(\%static_data_key);
$ >./test.pl
$VAR1 = {
'NAME' => [
'RAM',
'SHYAM',
'RAVI',
'HARI'
]
};

The return value of a Perl array in scalar context is the array's size. For example:
my #array = ( 'a', 'b', 'c' );
my $size = #array;
print "$size\n";
This code will print '3'. When dereferenced, anonymous arrays share this characteristic:
my $aref = [ 'a', 'b', 'c' ];
print $aref, "\n"; # ARRAY(0x1e33148)... useless in this case.
my $size = #{$aref}; # Dereference $aref, in scalar context.
print "$size\n"; # 3
The code I'm demonstrating takes a few unnecessary steps to lend clarity. Now consider this:
print scalar #{[ 'a', 'b', 'c']}, "\n"; # 3
Here we're constructing an anonymous array and immediately dereferencing it. We obtain its return value in scalar context, which happens to be 3.
Finally, let's put that anonymous array into a hash:
my %hash = (
NAME => [ 'joseph', 'frank', 'pete' ]
);
print scalar #{$hash{NAME}}, "\n";
Read that last line from the middle outward; first we obtain the value stored the NAME element within %hash. That is a reference to an anonymous array. So we dereference it with #{ ..... }. And we use scalar to force scalar context. The output is 3.

#!/usr/bin/perl
# your code goes here
use strict;
use warnings;
my %static_data_key = (
'NAME' =>['RAM','SHYAM','RAVI','HARI'],
);
print scalar #{$static_data_key{'NAME'}};
Demo

Related

How to grep undefined values in an array?

I have an array with an undefined value:
[0] 0.1431,
[1] undef
I then later push this value onto an array, and Perl doesn't warn me about the uninitialized values (I cannot imagine why someone would want this to happen without a warning or die, even when I have use autodie ':all' set, but that's another story)
So I try and grep on these arrays to prevent pushing undef values onto arrays, thus:
if (grep {undef} #array) {
next
}
But, this doesn't catch the value.
How can I grep for undefined values in Perl5?
grep for not defined instead:
use warnings;
use strict;
my #array = (0, undef, undef, 3);
if (grep {! defined} #array) {
print "undefined values in array\n";
}
There are several reasons as to why you'd want to have/allow undefined values in an array. One such beneficial scenario is when passing in positional parameters to a function.
Why not prevent the values from being added in the first place?
Replace
push #array, LIST;
with
push #array, grep defined, LIST;
If it's specifically a scalar, you could also use
push #array, $scalar if defined($scalar);
You can use defined to get rid of undefined items:
use warnings;
use strict;
use Data::Dumper;
my #a1 = (1,2,3,undef,undef,4);
print Dumper(\#a1);
my #a2 = grep { defined } #a1;
print Dumper(\#a2);
Output:
$VAR1 = [
1,
2,
3,
undef,
undef,
4
];
$VAR1 = [
1,
2,
3,
4
];

Perl hash, array and references

I have this 3 lines of code in a sub and I'm trying to write them together on one line only.. but I'm quite lost
my %p = #_;
my $arr = $p{name};
my #a = #$arr;
what's the correct way of doing this?
thank you!
my %p = #_;
#_ is assumed to contain key-value pairs which are then used to construct the hash %p.
my $arr = $p{name};
The argument list is assumed to have contained something along the lines of name, [1, 2, 3,] so that $p{name} is an reference to an array.
my #a = #$arr;
Dereference that array reference to get the array #.
Here is an invocation that might work with this prelude in a sub:
func(this => 'that', name => [1, 2, 3]);
If you want to reduce the whole prelude to a single statement, you can use:
my #a = #{ { #_ }->{name} };
as in:
#!/usr/bin/env perl
use strict;
use warnings;
use YAML::XS;
func(this => 'that', name => [1, 2, 3]);
sub func {
my #a = #{ { #_ }->{name} };
print Dump \#a;
}
Output:
---
- 1
- 2
- 3
If the array pointed to by name is large, and if you do not need a shallow copy, however, it may be better to just stick with references:
my $aref = { #_ }->{ name };
OK so what you're doing is:
Assign a list of elements passed to the sub, to a hash.
extract a value from that hash (that appears to be an array reference)
dereference that into a standalone array.
Now, I'm going to have to make some guesses as to what you're putting in:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub test {
my %p = #_;
my $arr = $p{name};
my #a = #$arr;
print Dumper \#a;
}
my %input = ( fish => [ "value", "another value" ],
name => [ "more", "less" ], );
test ( %input );
So with that in mind:
sub test {
print join "\n", #{{#_}->{name}},"\n";
}
But actually, I'd suggest what you probably want to do is pass in the hashref in the first place:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub test {
my ( $input_hashref ) = #_;
print Dumper \#{$input_hashref -> {name}};
}
my %input = ( fish => [ "value", "another value" ],
name => [ "more", "less" ], );
test ( \%input );
Also:
Don't use single letter variable names. It's bad style.
that goes double for a and b because $a and $b are for sorting. (And using #a is confusing as a result).

Printing Hash in Perl

I want to know the following code why print "2/8".
#!/usr/bin/perl
#use strict;
#use warnings;
%a = ('a'=>'dfsd','b'=>'fdsfds');
print %a."\n";
You are printing a hash in scalar context by concatenating it with a string '\n'
If you evaluate a hash in scalar context, it returns false if the hash
is empty. If there are any key/value pairs, it returns true; more
precisely, the value returned is a string consisting of the number of
used buckets and the number of allocated buckets, separated by a
slash.
2/8 means that of the 8 buckets allocated, 2 have been touched. Considering that you've inserted only 2 values, it is doing well so far :)
The value is obviously of no use, except to evaluate how well the hash-function is doing. Use print %a; to print its contents.
As mentioned by #Dark.. you are printing a hash in scalar context.
if you really want to print a hash, then use Data::Dumper
use Data::Dumper;
...
...
print Dumper(%a);
for eg:
use Data::Dumper;
my %hash = ( key1 => 'value1', key2 => 'value2' );
print Dumper(%hash); # okay, but not great
print "or\n";
print Dumper(\%hash); # much better
And the output:
$VAR1 = 'key2';
$VAR2 = 'value2';
$VAR3 = 'key1';
$VAR4 = 'value1';
or
$VAR1 = {
'key2' => 'value2',
'key1' => 'value1'
};

Slicing a nested hash in Perl

Say I have a hash that I can index as:
$hash{$document}{$word}
From what I read online (although I could not find this on perlreftut, perldsc or perllol), I can slice a hash using a list if I use the # prefix on my hash to indicate that I want the hash to return a list. However, if I try to slice my hash using a list #list:
#%hash{$document}{#list}
I get several "Scalar values ... better written" errors.
How can I slash a nested hash in Perl?
The sigill for your hash must be #, like so:
#{$hash{$document}}{#list}
Assuming #list contains valid keys for %hash it will return the corresponding values, or undef if the key does not exist.
This is based on the general rule of a hash slice:
%foo = ( a => 1, b => 2, c => 3 );
print #foo{'a','b'}; # prints 12
%bar = ( foo => \%foo ); # foo is now a reference in %bar
print #{ $bar{foo} }{'a','b'}; # prints 12, same data as before
First, when you expect to get a list from a hash slice, use # sigil first. % is pointless here.
Second, you should understand that $hash{$document} value is not a hash or array. It's a reference - to a hash OR to an array.
With all this said, you might use something like this:
#{ $hash{$document} }{ #list };
... so you dereference value of $hash{$document}, then use a hash slice over it. For example:
my %hash = (
'one' => {
'first' => 1,
'second' => 2,
},
'two' => {
'third' => 3,
'fourth' => 4,
}
);
my $key = 'one';
my #list = ('first', 'second');
print $_, "\n" for #{ $hash{$key} }{#list};
# ...gives 1\n2\n

What's the point of this kind of code?

{%{$self->param}}
It does hash expand, and then create another hash reference.
But isn't {%{$self->param}} the same as $self->param? Why does the code bother doing the trick?
It copies the hash. Consider the following snippet:
use Data::Dumper;
my $foo = { a => 1, ar => [1] };
my $bar = {%$foo};
$bar->{b} = 2;
push #{$bar->{ar}}, 4;
print Dumper $foo;
print Dumper $bar;
It prints
$VAR1 = {
'a' => 1,
'ar' => [
1,
4
]
};
$VAR1 = {
'a' => 1,
'b' => 2,
'ar' => [
1,
4
]
};
So you can see that the copy is shallow: Scalars are copied over, even if they are references. The referenced objects are the same (in this example the array referenced by ar).
Although both {%{$self->param}} and $self->param are references to a hash, they do not refer to a hash stored in the same location.
The first expression dereferences $self->param to a hash, and returns a reference to an anonymous hash. Within the outer braces, %{$self->param} is actually expanded and copied temporarily, and then a reference to this temporary copy is returned, not to the old hash.
This code actually creates a copy hash (shallow copy of keys and values, but not deep copy), reference to which is returned and returns reference to it.
If some sub returns reference to a hash and you change something in it, you actually are changing values in original hash. To avoid this we sometimes need to copy whole hash (or array) before making any changes.
Here's example:
sub get_hashref {
my $hashref = shift;
return $hashref;
}
my %hash = (foo => 'bar');
my $ref = get_hashref(\%hash);
$ref->{foo} = 'baz'; # Changes 'foo' value in %hash
print "Original 'foo' now is: $hash{foo}\n"; # 'baz'
print "Ref's 'foo' now is: $ref->{foo}\n"; # 'baz'
# But!
$ref = {%{ get_hashref(\%hash) }};
$ref->{foo} = 42; # No changes in %hash
print "Original 'foo' now is: $hash{foo}\n"; # 'baz'
print "Ref's 'foo' now is: $ref->{foo}\n"; # '42'
To be understood better, {%{ $self->param }} may be expanded to:
my $ref = $self->param; # Ref to original hash
my %copy = %{$ref}; # Copies keys and values to new hash
my $ref_to_copy = {%copy}; # get ref to it
You also may omit last step if you need hash but not reference to it.