How to pass value of a hash of arrays into a subroutine? - arrays

I have a hash of arrays. I'm trying to do the following: as I'm looping through the keys of the hash, I would like to pass the value of the hash (in this case the array) into a subroutine.
Once I'm in the subroutine, I'd like to do a bunch of calculations with the array including take the average of the numeric values in the array. Lastly return a value based on the calculations.
Here's a minimal representation of what I have:
#!/usr/bin/perl
use warnings; use strict;
use List::Util qw(sum);
%data_set = (
'node1' => ['1', '4', '5', '2'],
'node2' => ['3', '2', '4', '12'],
'node3' => ['5', '2', '3', '6'],
);
foreach my $key ( keys %data_set ) {
foreach ( #{$data_set{$key}} ) {
my #array = split; # it's not letting me
calculate(\#array); ### I'm getting the error here!!
}
}
sub calculate{
my #array = #{$_};
my $average = mean(\#array);
# do some more calculations
return; # return something
}
# sum returns the summation of all elements in the array
# dividing by #_ (which in scalar content gives the # of elements) returns
# the average
sub mean{
return sum(#_)/#_;
}
Quick clarification: On the first iteration node1, I'd like to pass the array '1', '4', '5', '2' into the subroutine.
I think for my purposes this might be a bit more efficient than passing a reference to the hash of arrays into each subroutine. What do you guys think? In any case, can you guys help me figure this out? Any suggestions are appreciated. thanks

I think you are a bit confused about when you need to reference and dereference variables as well as what you are passing, where.
Lets look at this closer,
foreach my $key ( keys %data_set ) {
foreach ( #{$data_set{$key}} ) {
my #array = split; # it's not letting me
calculate(\#array); ### I'm getting the error here!!
}
}
You loop over the hash to get the keys, but then you use them to access the values to dereference the arrays and split each array into a single element array (of a string). Then you pass a reference to that 1-element array to calculate. Basically, you are doing too much work if the only thing that you want to do is pass each value of the hash (the arrays) into the calculate subroutine. Try something like this:
foreach my $key (keys %data_set){
calculate($data_set{$key}); # This is already a reference to an array
}
Additionally, in calculate you need to change my #array = #{$_}; to either my #array = #{$_[0]}; or my #array = #{(shift)};

Your mean subroutine should look something like this:
sub mean{
my #array = #{(shift)};
return sum(#array)/scalar(#array);
}

Related

Always treat hash element as array?

If I have a hash of data from a JSON import, is there a neat way to handle cases where an element could be either a value or an array of values?
So it could be
'blah' => [1,2,3,4,5]
or
'blah' => 1
Can I 'force' blah to be an array, even if it's not, so I can iterate over it and not worry about the number of elements?
I thought I could possibly push the contents of blah onto an empty array which would either push the single value onto the array or join the two arrays together. Is there a neat/best way to do this?
Assuming it will always be either a scalar number or an array reference:
Test, explicitly, if it is a reference. If it is, then assign it back to itself. Otherwise, wrap it in an array reference and assign that instead.
$foo{blah} = (ref $foo{blah}) ? $foo{blah} : [ $foo{blah} ];
It is not clear what you try to achieve, more details with sample code demonstrating the usage would be more helpful
use strict;
use warnings;
use Data::Dumper;
my $debug = 0;
my %hash = ( 'a' => 1,
'b' => [2,3,4,5,6,7,8,9],
'c' => { 'x' => 'one', 'y' => 'two', 'z' => 'three' }
);
print Dumper(\%hash) if $debug;
while( my ($k,$v) = each %hash ){
print "[variable] $k = $v\n" if ref $v eq '';
print "[array ] $k\n", Dumper($v) if ref $v eq 'ARRAY';
print "[hash ] $k\n", Dumper($v) if ref $v eq 'HASH';
}
Please visit following webpage, I believe you will find it useful

Perl: Adding more elements to array created from hash

I have a perl script where we are converting Hash into array-
my %info = (
'name' => $1,
'ip' => $2,
'parent' => '',
'isLocal' => 0,
'clip' => 0,
);
my #siteinfos = ();
push( #siteinfos, \%info );
Now I am able to accesss the array by-
foreach my $site(#siteinfos) {
if ( $site->{'name'} eq $sitename) {
.....
}
First I am not sure how this conversion actually works.Secondly,Now I want to add more elements to this array in (key,pair) format.How can I do so?
First, there is no conversion.
my #siteinfos = ();
defines an array #siteinfos and sets to the empty list. Of course, you could have achieved the same result by using just
my #siteinfos;
Next, the statement
push( #siteinfos, \%info );
pushes a reference to the hash %info to #siteinfos as the sole element of that array.
Next, the loop
foreach my $sinfo (#siteinfos) { # assuming original is a typo
ends up aliasing $sinfo to that sole hash reference in #siteinfos. In the body of the loop, you access an element of the original hash via the reference you pushed to #siteinfos.
Now, things would have been different if you had done:
push #siteinfos, %info;
That would have set #siteinfos to a list like the following (the order is non-deterministic):
$VAR1 = [
'ip',
undef,
'clip',
0,
'isLocal',
0,
'name',
undef,
'parent',
''
];
The undef are there because the match variables were empty when I ran the script. Now, if you want to push another key-value pair to this array, that's trivial:
push #siteinfos, $key, $value;
But of course, by assigning the hash to an array, you are losing the ability to simply look up values by key. In addition, you can keep pushing duplicate keys to an array, but if you assign it back to another hash:
my %otherinfo = #siteinfos;
then only the last version of an even-indexed element (key) and its corresponding odd-indexed element (value) will survive.
You are not converting anything. You are making a new array named #siteinfos.
my #siteinfos = ();
Then you add one entry, which is a reference to the hash %info.
push( #siteinfos, \%info );
The array now holds a reference to %info. That's $siteinfo[0].
In your loop you iterate all the elements in #siteinfos. The hashref now goes into $sinfo.
If you want to add more keys and values to that hashref, just put them in.
foreach my $sinfo (#siteinfos) {
if ( $sinfo->{'name'} eq $sitename) {
$sinfo->{foo} = "bar"; # as simple as this
}
}

Perl - How do I update (and access) an array stored in array stored in a hash?

Perhaps I have made this more complicated than I need it to be but I am currently trying to store an array that contains, among other things, an array inside a hash in Perl.
i.e. hash -> array -> array
use strict;
my %DEVICE_INFORMATION = {}; #global hash
sub someFunction() {
my $key = 'name';
my #storage = ();
#assume file was properly opened here for the foreach-loop
foreach my $line (<DATA>) {
if(conditional) {
my #ports = ();
$storage[0] = 'banana';
$storage[1] = \#ports;
$storage[2] = '0';
$DEVICE_INFORMATION{$key} = \#storage;
}
elsif(conditional) {
push #{$DEVICE_INFORMATION{$key}[1]}, 5;
}
}#end foreach
} #end someFunction
This is a simplified version of the code I am writing. I have a subroutine that I call in the main. It parses a very specifically designed file. That file guarantees that the if statement fires before subsequent elsif statement.
I think the push call in the elsif statement is not working properly - i.e. 5 is not being stored in the #ports array that should exist in the #storage array that should be returned when I hash the key into DEVICE_INFORMATION.
In the main I try and print out each element of the #storage array to check that things are running smoothly.
#main execution
&someFunction();
print $DEVICE_INFORMATION{'name'}[0];
print $DEVICE_INFORMATION{'name'}[1];
print $DEVICE_INFORMATION{'name'}[2];
The output for this ends up being... banana ARRAY(blahblah) 0
If I change the print statement for the middle call to:
print #{$DEVICE_INFORMATION{'name'}[1]};
Or to:
print #{$DEVICE_INFORMATION{'name'}[1]}[0];
The output changes to banana [blankspace] 0
Please advise on how I can properly update the #ports array while it is stored inside the #storage array that has been hash'd into DEVICE_INFORMATION and then how I can access the elements of #ports. Many thanks!
P.S. I apologize for the length of this post. It is my first question on stackoverflow.
I was going to tell you that Data::Dumper can help you sort out Perl data structures, but Data::Dumper can also tell you about your first problem:
Here's what happens when you sign open-curly + close-curly ( '{}' ) to a hash:
use Data::Dumper ();
my %DEVICE_INFORMATION = {}; #global hash
print Dumper->Dump( [ \%DEVICE_INFORMATION ], [ '*DEVICE_INFORMATION ' ] );
Here's the output:
%DEVICE_INFORMATION = (
'HASH(0x3edd2c)' => undef
);
What you did is you assigned the stringified hash reference as a key to the list element that comes after it. implied
my %DEVICE_INFORMATION = {} => ();
So Perl assigned it a value of undef.
When you assign to a hash, you assign a list. A literal empty hash is not a list, it's a hash reference. What you wanted to do for an empty hash--and what is totally unnecessary--is this:
my %DEVICE_INFORMATION = ();
And that's unnecessary because it is exactly the same thing as:
my %DEVICE_INFORMATION;
You're declaring a hash, and that statement fully identifies it as a hash. And Perl is not going to guess what you want in it, so it's an empty hash from the get-go.
Finally, my advice on using Data::Dumper. If you started your hash off right, and did the following:
my %DEVICE_INFORMATION; # = {}; #global hash
my #ports = ( 1, 2, 3 );
# notice that I just skipped the interim structure of #storage
# and assigned it as a literal
# * Perl has one of the best literal data structure languages out there.
$DEVICE_INFORMATION{name} = [ 'banana', \#ports, '0' ];
print Data::Dumper->Dump(
[ \%DEVICE_INFORMATION ]
, [ '*DEVICE_INFORMATION' ]
);
What you see is:
%DEVICE_INFORMATION = (
'name' => [
'banana',
[
1,
2,
3
],
'0'
]
);
So, you can better see how it's all getting stored, and what levels you have to deference and how to get the information you want out of it.
By the way, Data::Dumper delivers 100% runnable Perl code, and shows you how you can specify the same structure as a literal. One caveat, you would have to declare the variable first, using strict (which you should always use anyway).
You update #ports properly.
Your print statement accesses $storage[1] (reference to #ports) in wrong way.
You may use syntax you have used in push.
print $DEVICE_INFORMATION{'name'}[0], ";",
join( ':', #{$DEVICE_INFORMATION{'name'}[1]}), ";",
$DEVICE_INFORMATION{'name'}[2], "\n";
print "Number of ports: ", scalar(#{$DEVICE_INFORMATION{'name'}[1]})),"\n";
print "First port: ", $DEVICE_INFORMATION{'name'}[1][0]//'', "\n";
# X//'' -> X or '' if X is undef

Loop 1st element of 2d List

I have a list like this
[[hash,hash,hash],useless,useless,useless]
I want to take the first element of hashes and loop through it - i try this:
my #list = get_list_somehow();
print Dumper($list[0][0]); print Dumper($list[0][1]);print Dumper($list[0][2]);
and i am able to access the elements fine manually, but when i try this
my #list = get_list_somehow()[0];
print Dumper($list[0]); print Dumper($list[1]);print Dumper($list[2]);
foreach(#list){
do_something_with($_);
}
only $list[0] returns a value (the first hash, everything else is undefined)
You are taking a subscript [0] of the return value of get_list_somehow() (although technically, you need parentheses there). What you need to do is to dereference the first element in that list. So:
my #list = get_list_somehow();
my $first = $list[0]; # take first element
my #newlist = #$first; # dereference array ref
Of course, this is cumbersome and verbose, and if you just want to print the array with Data::Dumper you can just do:
print Dumper $list[0];
Or if you just want the first array, you can do it in one step. Although this looks complicated and messy:
my #list = #{ (get_list_somehow())[0] };
The #{ ... } will expand an array reference inside it, which is what hopefully is returned from your subscript of the list from get_list_somehow().
I'm taking that your sample data looks like this:
my #data = [
{
one => 1,
two => 2,
three => 3,
},
"value",
"value",
"value",
];
That is, the first element of #data, $data[0] is your hash. Is that correct?
Your hash is a hash reference. That is the $data[0] points to the memory location where that hash is stored.
To get the hash itself, it must be dereferenced:
my %hash = %{ $data[0] }; # Dereference the hash in $data[0]
for my $key ( keys %hash ) {
say qq( \$hash{$key} = "$hash{$key}".);
}
I could have done the dereferencing in one step...
for my $key ( keys #{ $data[0] } ) {
say qq(\$data[0]->{$key} = ") . $data[0]->{$key} . qq(".);
}
Take a look at the Perl Reference Tutorial for information on how to work with references.
I'm guessing a bit on your data structure here:
my $list = [
[ { a => 1,
b => 2,
c => 3, },
{ d => 4, }
{ e => 5, }
], undef, undef, undef,
];
Then we get the 0th (first) element of the top-level array reference, which is another array reference, and then the 0th (first) element of THAT array reference, which is the first hash reference:
my $hr = $list->[0][0];
And iterate over the hash keys. That could also be written as one step: keys %{ $list->[0][0] }. It's a bit easier to see what's going on when broken out into two steps.
for my $key (keys %$hr) {
printf "%s => %s\n", $key, $hr->{$key};
}
Which outputs:
c => 3
a => 1
b => 2

How would I use a hash slice to initialize a hash stored in a data structure?

In an earlier question I asked how to initialize a Perl hash using slices. It is done like this:
my %hash = ();
my #fields = ('currency_symbol', 'currency_name');
my #array = ('BRL','Real');
#hash{#fields} = #array;
Now let's imagine a more complex hash, and here is how it is initialized:
my %hash = ();
my $iso = 'BR';
$hash->{$iso}->{currency_symbol} = 'BRL';
$hash->{$iso}->{currency_name} = 'Real';
print Dumper($hash);
This results in the following:
$VAR1 = {
'BR' => {
'currency_symbol' => 'BRL',
'currency_name' => 'Real'
}
};
Now the question would be: how to initialize this particular hash using the splice method?
The perllol documentation's Slices section covers array slices:
If you want to get at a slice (part of a row) in a multidimensional array, you're going to have to do some fancy subscripting. That's because while we have a nice synonym for single elements via the pointer arrow for dereferencing, no such convenience exists for slices. (Remember, of course, that you can always write a loop to do a slice operation.)
Here's how to do one operation using a loop. We'll assume an #AoA variable as before.
#part = ();
$x = 4;
for ($y = 7; $y < 13; $y++) {
push #part, $AoA[$x][$y];
}
That same loop could be replaced with a slice operation:
#part = #{ $AoA[4] } [ 7..12 ];
Extrapolating to hash slices, we get
#{ $hash{$iso} }{#fields} = #array;
You know it's a hash slice because the “subscripts” are surrounded with curly braces rather than square brackets.
First of all, since your hash is declared %hash, it would just be $hash{ $iso }. $hash->{ $iso } refers to a slot in the hash pointed to by $hash, which may or may not be pointing to %hash.
But once you have that, you can do the following:
#{ $hash{ $iso } }{ #fields } = qw<BRL Real>;
But as levels soon get complex, it's better to forgo the autovivification luxury and do the following:
my $h = $hash{ $iso }{blah}{blah} = {};
#$h{ #field_names } = #field_values;
Relocatable pointers within the hierarchy of hashes makes it easier to write anonymous accesses that also allow for easy slices.
$hash{$iso} is going to be a hash reference. You replace what would be the variable name (without the sigil) in a simple slice with a block containing the reference, so:
#array{#list}
becomes
#{ $hash{$iso} }{#list}
See http://perlmonks.org/?node=References+quick+reference

Resources