Perl - Return an array of hashes - perl

i have an array of hashes to be returned.
before returning the array i cross checked it. it was working fine.
but after returning the array of hashess to the calling sub, i am not able to read it.
plz find the below code for referrence.. and do let me know how to read/ return an array of hashes
Thanks... :)
#!/usr/bin/perl
use strict;
use warnings;
# Subroutine prototypes
sub get_two_arrays();
my #one=();
#one = get_array_Hashes();
print "\n First: #one->{Version}\n"; // Printing the return array of hashes
sub get_array_Hashes() {
my #dotNetHashArray =();
my $dotNetHash1 = {Version => "Test-1 Version", SP => "installedSp", Build => "installedBuild"};
push #dotNetHashArray, $dotNetHash1;
my $dotNetHash2 = {Version => "Test-2 Version", SP => "installedSp", Build => "installedBuild"};
push #dotNetHashArray, $dotNetHash2;
my $dotNetHash3 = {Version => "Test-3 Version", SP => "installedSp", Build => "installedBuild"};
push #dotNetHashArray, $dotNetHash3;
print "Test Array of hashes before return";
for(my $i=0; $i<#dotNetHashArray; $i++)
{
print("\n Hash Value : ".$dotNetHashArray[$i]->{Version});
}
return \#dotNetHashArray
}

Perl isn't C, and prototypes are meant for something very different and special. If you don't know what niche purpose they serve then never use them
Similarly there is no reason to pre-declare a subroutine before calling it. As long as you don't use prototypes Perl will do the right thing
There is also no reason to initialise arrays when you declare them if you want them empty. That is what Perl does by default
People familar with Perl would thank you for using lower-case and underscore identifiers for variables and subroutines. Camel case is usually reserved for package names
As others have said, you are returning a reference to an array. But instead of dereferencing the return value it is probably better if you keep it as a reference and use it as such. The only change necessary is to iterate over the array that is returned
Here is a more canonical form of your program which I hope will help
use strict;
use warnings;
my $one = get_array_Hashes();
print "\nArray of hashes after return\n";
print "First: $_->{Version}\n" for #$one;
sub get_array_Hashes {
my #dotnet_hash_array;
my $dotnet_hash1 = {
Version => "Test-1 Version",
SP => "installedSp",
Build => "installedBuild"
};
push #dotnet_hash_array, $dotnet_hash1;
my $dotnet_hash2 = {
Version => "Test-2 Version",
SP => "installedSp",
Build => "installedBuild"
};
push #dotnet_hash_array, $dotnet_hash2;
my $dotnet_hash3 = {
Version => "Test-3 Version",
SP => "installedSp",
Build => "installedBuild"
};
push #dotnet_hash_array, $dotnet_hash3;
print "Test Array of hashes before return\n";
for my $i (0 .. $#dotnet_hash_array) {
print "Hash Value : $dotnet_hash_array[$i]->{Version}\n";
}
return \#dotnet_hash_array
}
output
Test Array of hashes before return
Hash Value : Test-1 Version
Hash Value : Test-2 Version
Hash Value : Test-3 Version
Array of hashes after return
First: Test-1 Version
First: Test-2 Version
First: Test-3 Version

You are returning a reference to an array:
return \#dotNetHashArray
you have to
#one = #{ get_array_Hashes() };
to dereference it.
In addition
the // comment will not work (use #)
usually you don't need to use prototypes in Perl (see Why are Perl 5's function prototypes bad?)
you will need a loop also after the return to print out the values
you don't need a cursor variable to iterate over arrays in Perl
for my $item (#dotNetHashArray) {
print "\n Hash Value: $item->{Version}";
}
if you need to have the \n at the beginning of your prints you are a missing a \n after the loop
You will end up with:
#!/usr/bin/perl
use strict;
use warnings;
# do not use function prototypes
# perl subroutines are usually all lowercase (no camel-case)
sub get_array_hashes {
my #dot_net_hash_array = ();
# actually you don't need to create a local variable for each item you push
push #dot_net_hash_array, {
# avoid unncessary string interpolation (use ' if no variables in the string have to be interpolated)
version => 'Test-1 Version',
sp => 'installedSp',
build => 'installedBuild'
};
push #dot_net_hash_array,
{
version => 'Test-2 Version',
sp => 'installedSp',
build => 'installedBuild'
};
push #dot_net_hash_array,
{
version => 'Test-3 Version',
sp => 'installedSp',
build => 'installedBuild'
};
print "Test Array of hashes before return\n";
for my $item (#dot_net_hash_array) {
print "Hash Value : $item->{version}\n";
}
return \#dot_net_hash_array;
}
my #one = #{ get_array_hashes() };
# Use # for comments
# Printing the return array of hashes
print "Test Array of hashes after return\n";
for my $item (#one) {
print "Hash Value : $item->{version}\n";
}

Related

How do I interpolate the value in a string variable into a constant in Perl?

I am writing a function in Perl, where a string is passed as an argument, and I need to interpret the string into the referenced value. The string would look something like this:
"Edible => 1;Fruit => STRAWBERRY;"
Now, the variable part will be stored using hashes, however, the value is already defined using constants. My question is, once I store the value into a temporary variable, how do I convert it into the value of the constant?
Here is some example code:
#!/usr/bin/perl
require Exporter;
our #ISA = 'Exporter';
our #EXPORT = qw(STRAWBERRY TANGERINE PEAR APPLE PERSIMMON FUNC_Interpreter);
use constant {
STRAWBERRY => 1
,TANGERINE => 2
,PEAR => 3
,APPLE => 4
,PERSIMMON => 5
};
sub FUNC_Interpreter {
my ($analyze_this) = #_;
my #values;
foreach my $str (split (/;/, $analyze_this)) {
my ($key, $value) = split /=>/, $str;
push (#values, #{[ $value ]}); # Problem line! I want to store the numeric value here. This doesn't work... :(
}
}
FUNC_Interpreter ("HELLO=>TANGERINE;HELLO=>STRAWBERRY");
So basically, what I want to do is convert a string, which is actually the name of a constant stored in a variable, into a constant's value. Is this possible?
Constants can be treated as subs.
{
no strict qw( refs );
push #values, $value->();
}
or
push #values, ( \&$value )->();
But that's a hackish risky approach. And the second version even hides that you are dangerously allowing the user to call any sub in any package. What I would do instead:
my %lookup;
BEGIN {
%lookup = (
STRAWBERRY => 1,
TANGERINE => 2,
PEAR => 3,
APPLE => 4,
PERSIMMON => 5,
);
}
use constant \%lookup;
push #values, $lookup{ $value };
Using this approach, inputs can be validated trivially, and invalid inputs merely result in undef.

Unblessing Perl objects and constructing the TO_JSON method for convert_blessed

In this answer I found a recommendation for a simple TO_JSON method, which is needed for serializing blessed objects to JSON.
sub TO_JSON { return { %{ shift() } }; }
Could anybody please explain in detail how it works?
I changed it to:
sub TO_JSON {
my $self = shift; # the object itself – blessed ref
print STDERR Dumper $self;
my %h = %{ $self }; # Somehow unblesses $self. WHY???
print STDERR Dumper \%h; # same as $self, only unblessed
return { %h }; # Returns a hashref that contains a hash.
#return \%h; # Why not this? Works too…
}
Many questions… :( Simply, I’m unable to understand 3-liner Perl code. ;(
I need the TO_JSON but it will filter out:
unwanted attributes and
unset attributes too (e.g. for those the has_${attr} predicate returns false)
This is my code – it works but I really don't understand why the unblessing works…
use 5.010;
use warnings;
use Data::Dumper;
package Some;
use Moo;
has $_ => ( is => 'rw', predicate => 1,) for (qw(a1 a2 nn xx));
sub TO_JSON {
my $self = shift;
my $href;
$href->{$_} = $self->$_ for( grep {!/xx/} keys %$self );
# Same mysterious unblessing. The `keys` automagically filters out
# “unset” attributes without the need of call of the has_${attr}
# predicate… WHY?
return $href;
}
package main;
use JSON;
use Data::Dumper;
my #objs = map { Some->new(a1 => "a1-$_", a2 => "a2-$_", xx=>"xx-$_") } (1..2);
my $data = {arr => \#objs};
#say Dumper $data;
say JSON->new->allow_blessed->convert_blessed->utf8->pretty->encode($data);
EDIT: To clarify the questions:
The %{ $hRef } derefences the $hRef (getting the hash pointed to by the reference), but why get a plain hash from a blessed object reference $self?
In other words, why the $self is a hashref?
I tried to make a hash slice like #{$self}{ grep {!/xx/} keys %$self} but it didn't work. Therefore I created that horrible TO_JSON.
If the $self is a hashref, why the keys %$self returns only attributes having a value, and not all declared attributes (e.g. the nn too – see the has)?
sub TO_JSON { return { %{ shift() } }; }
| | |
| | L_ 1. pull first parameter from `#_`
| | (hashref/blessed or not)
| |
| L____ 2. dereference hash (returns key/value list)
|
L______ 3. return hashref assembled out of list
In your TO_JSON() function { %h } returns a shallow hash copy, while \%h returns a reference to %h (no copying).
Perl implemented object orientation by simply making it possible for a reference to know which package it came from (with bless). Knowing that a reference came from the Foo package means that methods are really functions defined in that package.
Perl allows any kind of reference to get blessed; not just hash references. It's very common to bless hash references; a lot of documentation shows doing exactly that; and Moose does it; but, it's possible to bless an array reference, or a subroutine reference, or a filehandle, or a reference to a scalar. The syntax %{$self} only works on hash references (blessed or not). It takes the hash reference, and dereferences it as a hash. The fact that the original reference may have been blessed is lost.
I need the TO_JSON but what will filter out:
unwanted attributes
and unset attributes too (e.g. for those the has_${attr} predicate returns false.
Pre-5.20, hash slices only give you the values and not the keys from the original hash. You want both keys and values.
Assuming you have a hash, and want to filter out undef values and keys not on a whitelist, there are a few options. Here's what I have, using the JSON module:
use strict; # well, I used "use v5.18", but I don't know which version of Perl you're using
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my %whitelist = map { $_, 1 } qw{foo bar};
my %bar = map { $_ => $foo->{$_} }
grep { defined $foo->{$_} && exists $whitelist{$_} }
keys %$foo;
print to_json(\%bar) . "\n";
# well, I used say() instead of print(), but I don't know which version of Perl you're using
The maps and greps aren't necessarily pretty, but it's the simplest way I could think of to filter out keys not on the whitelist and elements without an undef value.
You could use an array slice:
use strict;
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my #whitelist = qw{foo bar};
my %filtered_on_keys;
#filtered_on_keys{#whitelist} = #$foo{#whitelist};
my %bar = map { $_ => $filtered_on_keys{$_} }
grep { defined $filtered_on_keys{$_} }
keys %filtered_on_keys;
print to_json(\%bar) . "\n";
Or if you like loops:
use strict;
use warnings;
use JSON;
my $foo = { foo => undef, bar => 'baz', quux => 5 };
my %whitelist = map { $_ => 1 } qw{foo bar};
my %bar;
while (my ($key, $value) = each %$foo) {
if (defined $value && exists $whitelist{$key}) {
$bar{$key} = $value;
}
}
print to_json(\%bar) . "\n";
It seems like a good time to bring up Larry wall's quote, "Perl is designed to give you several ways to do anything, so consider picking the most readable one."
However, I made a big point that not all objects are hashes. The appropriate way to get data from an object is through its getter functions:
use strict;
use warnings;
use JSON;
my $foo = Foo->new({ foo => undef, bar => 'baz', quux => 5 }); # as an example
my %filtered_on_keys;
#filtered_on_keys{qw{foo bar}} = ($foo->get_foo(), $foo->get_bar());
my %bar = map { $_ => $filtered_on_keys{$_} }
grep { defined $filtered_on_keys{$_} }
keys %filtered_on_keys;
print to_json(\%bar) . "\n";

How do I add a new Key,Value pair to a Hash in an array of hash in perl?

Hi I have a need to add a new key,value pair to the hash entries within an array of hashes.
Below is some sample code which does not work(simplified with only 1 array entry) The output of the print statement just contains the 1 entry.
my #AoH;
push #AoH, { TEST1 => 'testvalue' };
for my $hash (#AoH)
{
$hash{'TEST2'} = 'testvalue2';
print Dumper($hash);
}
What am I doing wrong?
Thank you.
This code looks a little strange so I am going to assume it was done like that for the purposes of showing it briefly here, but the main thing you need to do to fix your code is change:
$hash{'TEST2'} = 'testvalue2';
to:
$$hash{'TEST2'} = 'testvalue2';
or:
$hash->{'TEST2'} = 'testvalue2';
The extra '$' or '->' dereferences the hash reference '$hash'. Since neither is there, it treats $hash{'TEST2'} as a different variable: '%hash' (not '$hash') and assigns 'testvalue2' to that. You would have gotten a good error message:
Global symbol "%hash" requires explicit package name at - line XX
if you tried to run this code with:
use strict;
use warnings;
at the beginning... which you should always do, so do that every time from now on.
use strict;
use warnings;
use Data::Dumper;
my #AoH=();
my %data_source_hash=(
TEST1 => 'testvalue1',
TEST2 => 'testvalue2'
);
# adds whole hash as the array element
push #AoH,{ %data_source_hash };
print Dumper(#AoH);
#AoH=();
print "---------------------------\n";
# adds each hash $key, $value pair as an element
while ( my ($key, $value) = each %data_source_hash )
{
push #AoH, { $key => $value };
}
print Dumper(#AoH);
#AoH=();
print "---------------------------\n";
# adds extra hash entry to each array element
push #AoH, { TEST1 => 'testvalue' };
push #AoH, { TEST3 => 'testvalue3' };
foreach my $el (#AoH)
{
my $key = 'TEST2';
$$el{$key} = $data_source_hash{$key};
}
print Dumper(#AoH);

perl how to reference hash itself

This might seem to be an odd thing to do, but how do I reference a hash while 'inside' the hash itself? Here's what I'm trying to do:
I have a hash of hashes with a sub at the end, like:
my $h = { A => [...], B => [...], ..., EXPAND => sub { ... } };
. I'm looking to implement EXPAND to see if the key C is present in this hash, and if so, insert another key value pair D.
So my question is, how do I pass the reference to this hash to the sub, without using the variable name of the hash? I expect to need to do this to a few hashes and I don't want to keep having to change the sub to reference the name of the hash it's currently in.
What you've got there is some nested array references, not hashes. Let's assume you actually meant that you have something like this:
my $h = { A => {...}, B => {...}, ..., EXPAND() };
In that case, you can't reference $h from within its own definition, because $h does not exist until the expression is completely evaluated.
If you're content to make it two lines, then you can do this:
my $h = { A=> {...}, B => {...} };
$h = { %$h, EXPAND( $h ) };
The general solution is to write a function that, given a hash and a function to expand that hash, returns that hash with the expansion function added to it. We can close over the hash in the expansion function so that the hash's name doesn't need to be mentioned in it. That looks like this:
use strict;
use warnings;
use 5.010;
sub add_expander {
my ($expanding_hash, $expander_sub) = #_;
my $result = { %$expanding_hash };
$result->{EXPAND} = sub { $expander_sub->($result) };
return $result;
}
my $h = add_expander(
{
A => 5,
B => 6,
},
sub {
my ($hash) = #_;
my ($maxkey) = sort { $b cmp $a } grep { $_ ne 'EXPAND' } keys %$hash;
my $newkey = chr(ord($maxkey) + 1);
$hash->{$newkey} = 'BOO!';
}
);
use Data::Dumper;
say Dumper $h;
$h->{EXPAND}->();
say Dumper $h;
Notice that we are creating $h but that the add_expander call contains no mention of $h. Instead, the sub passed into the call expects the hash it is meant to expand as its first argument. Running add_expander on the hash on the sub creates a closure that will remember which hash the expander is associated with and incorporates it into the hash.
This solution assumes that what should happen when a hash is expanded can vary by subject hash, so add_expander takes an arbitrary sub. If you don't need that degree of freedom, you can incorporate the expansion sub into add_expander.
The hash being built (potentially) happens after EXPAND() runs. I would probably use something like this:
$h = EXPAND( { A=>... } )
Where EXPAND(...) returns the modified hashref or a clone if the original needs to remain intact.

Can't print the size of a hash of hashes or access element within it

I have a hash %AllData pulling data from database using following code:
while(my $Row = $sth1->fetchrow_hashref())
{
if(defined($Row->{LotID}))
{
$AllData->{$Row->{LotID}}->{$Row->{Name}} = $Row->{Details};
}
}
After I'm done with pulling data, I use print Dumper($AllData); to print out All my data which show like:
$VAR1 = {
'4197359' => {
'Short Description' => 'Rock Picker',
'Lot Number' => '1065',
'Description' => 'Rock Picker'
},
'4194148' => {
'Short Description' => 'Large work bench w/ drawers',
'Lot Number' => '1041',
'Description' => 'Large work bench w/ drawers'
},
'4200944' => {
'Lot Number' => '1084',
'Description' => 'Horse Trailer'
}
}
However, when I try to print out the size of the hash or use foreach to access the hash, it shows 0 size and can't access any element within the hash:
print "Hash Size: ", scalar keys %AllData, "\n";
shows:
Hash Size: 0
What's the cause of my problem?
There is no hash %AllData, and if your program didn't raise an error then you haven't got
use strict;
use warnings;
at the head of you program. This is vital for all Perl programs, especially when you are asking others for help with your code.
The hash you're interested in is the one referenced by $AllData, so you need to use this variable and dereference it. Like this
print "Hash Size: ", scalar keys %$AllData, "\n";
Try accessing scalar keys %$AllData in order to access the hash that the reference.. refers to.
$AllData (what you're passing to Dumper() ) is a reference to a hash (a 'hashref')
%AllData is a different thing to Perl than $AllData. If this hasn't been set yet and perl isn't complaining, you may need to try putting use strict; at the top of your script so that you can be warned of these (and other) types of errors.
Maybe you need to dereference the hash first:
print "Hash Size: ", scalar keys %{ $AllData }, "\n";
should represent a hash ref as $%hash instead of %hash to print