Accessing and displaying key-value pairs in Perl from subroutine - perl

New to Perl. Got syntax errors in accessing key-value pairs from subroutine.
sub displayObj{
my %obj = shift; //the hash. It is a JSON object after decode_json()
my $field = shift; //the key we are searching. It is a string.
my $serialized = "{}"; //Initialization
foreach my $key (keys %obj){
if($key eq $field){
$serialized = $obj[{$field}];
}
}
print "$serialized\n";
}
It is giving me a compilation error in the if block.
I would like to know:
Should I use % or $ in my %obj?
How to access the key-value pair (also a JSON object) and assign it to $serialized using $obj?

I think you're trying to write a subroutine that displays the value of a hash element given its key
But you're missing the basic purpose of hashes: they're content-addressable. That means there's no need to iterate through all the keys of a hash comparing them to the string you're looking for, you can write just $hash{key} and the search will be done for you very quickly using a hashing system (hence the name of the data type). This is just the same as using $array[$i] to access an array element directly instead of looping over all of the indices of the array comparing each one to $i until you find the element you're looking for
If you're really dealing with objects then you shouldn't be accessing their internal data like this anyway. An object will have accessor methods that return the values you're supposed to be using; anything else is part of the internal working of the class and is meant to be private
The syntax error is in this line
$serialized = %obj[{$field}]
where it looks like you're trying to use both a hash key {...} and an array index [...]. That won't work
You don't show how you're calling your subroutine, but I imagine you're passing a reference to a hash, which is a scalar value and must be treated as such inside the subroutine
This program shows a working version of what I think you intended
use strict;
use warnings 'all';
my $obj = {
aa => 1,
cc => 2,
};
displayObj($obj, 'cc');
displayObj($obj, 'bb');
sub displayObj {
my $obj = shift; # The hash. It is a JSON object after decode_json()
my $field = shift; # The key we are searching. It is a string.
my $serialized = '{}'; # Initialization
foreach my $key ( keys %$obj ) {
if ( $key eq $field ) {
$serialized = $obj->{$field};
}
}
print "$serialized\n";
}
output
2
{}
But the loop can be removed altogether as I described, leaving
sub displayObj {
my ($obj, $field) = #_;
my $serialized = $obj->{$field} // '{}';
print "$serialized\n";
}
which produces exactly the same result. In fact there's little point in creating a subroutine to do this; you can write just
print $obj->{bb} // '{}', "\n"
with the same effect

I usually do this way:
sub displayObj{
my $objref = shift;
my %obj = %{$objref};
}

Related

Passing a hash to a subroutine without changing it input

I'm trying to debug some strange behavior while handling with a hash in Perl.
I'm passing a hash (not ref) to a subroutine and for some reason it updates it.
some_sub($a,%{$hash});
sub some_sub {
my ($a,%hash) = #_;
my #struct;
while (my ($dir, $data) = each %hash) {
foreach my $id (keys(%{$data})) {
my $entry = $data->{$id};
$entry->{id} = $id;
my $parent = $data->{$entry->{id}};
unless ($parent) {
push #struct, $entry
} else {
push #{$parent->{children}},$entry;
}
}
}
}
my %h= %{$hash};
print Dumper(\%h);
The sub some_sub does change %hash but only for the inner scope, so it should not change the data of the outside %hash. Also, I pass the hash as a hash and not as a hash ref. I suspected the sub some_sub inserts memory addresses into the %hash, but I'm not sure.
How should I debug and solve this issue?
EDIT: I also tried to pass a hash ref to the subroutine and do a dereferencing of the hash ref into another hash while doing all of the operations on the new hash.
Every value in a hash is a scalar. If you have a nested hash, the inner hash is stored as a scalar - a hash reference. Therefore, when changing the nested structures, the changes happen in the referenced hash, which is referenced from the original hash, too.
#! /usr/bin/perl
use warnings;
use strict;
sub change {
my %hash2 = #_;
for my $key (keys %hash2) {
++$_ for values $hash2{$key};
}
}
my %hash = (a => {b => 12, c => 24});
change(%hash);
use Data::Dumper; print Dumper \%hash;
Output:
$VAR1 = {
'a' => {
'b' => 13,
'c' => 25
}
};
The process of obtaining a structure that's similar as the original but contains different references is called cloning or deep copying. See Clone or dclone from Storable.
Arguments are passed to a function as a flat list of scalars, so
some_sub($a, %{$hashref})
has the keys and values of the hash passed as a list after $a
some_sub($a, key, value, ...);
since a function call always takes merely a list of scalars.
These key-value pairs are assigned to a hash in the function so when you work with that hash you directly use references from the calling code, your hash values. So data in the caller gets changed if those references are written to.
The details aren't given but in general one way to avoid changing caller's data in the sub is by introducing local variables for each reference the processing encounters. But then those may themselves contain references so you'd still need to be very careful.
It is simpler to make a full deep copy of the hash, ff the data structure isn't huge. For example
use Storable qw(dclone);
some_sub($v, $hashref);
sub some_sub {
my ($var, $hr) = #_;
my $cloned_hashref = dclone($hr);
# work away with $cloned_hashref
}

How to access hash from object

I have written a small class which just got some getter and setter methods. One of those Properties is a hash.
sub getMyData
{
my $objekt = shift;
return $objekt->{MYDATA};
}
sub setMyData
{
my $objekt = shift;
my %myData= shift;
$objekt->{MYDATA} = \%myData;
}
If i set the value like this in another skript which access my class:
my %test;
$test{'apple'}='red';
$objekt = MYNAMESPACE::MYCLASS->new;
$objekt->setMyData(%test);
I thought i can access this value easy via:
my $data = $objekt->getMyData;
print $data{'apple'};
I just get undef value.
Output from Dumper:
Can someone tell me what's wrong here and how i can access getMyData and print the value 'red'?
shift removes and returns the first element of an array. Inside of a subroutine a bare shift operates on #_, which contains a copy of all arguments passed to that subroutine.
What is really happening here is that setMyData is being passed this data:
setMyData($objekt, 'apple', 'red');
The first shift in setMyData removes $objekt from #_
The second shift in setMyData removes 'apple', but since you assign the result of this shift to a Hash it creates a Hash that looks like this: 'apple' => undef
You take a reference to this Hash and store it in the MYDATA key of $objekt
What you really want is to assign the remainder of #_ to your Hash:
sub setMyData {
my $objekt = shift;
my %myData = #_;
# my ($objekt, %myData) = #_; (alternative)
$objekt->{MYDATA} = \%myData;
}
Another option is to instead send a Hash reference to setMyData, which would work with shift:
sub setMyData {
my $objekt = shift;
my $myData_ref = shift
$objekt->{MYDATA} = $myData_ref;
}
$objekt->setMyData(\%test);
You are missing the dereference arrow. Because you put a hashref (\%myData) in, you also get a reference out.
my $data = $objekt->getMyData;
print $data->{'apple'};
# ^
# here
You also need to change the assignment, because you are passing a list to the setter, not a reference. shift is for scalar (single) values, but %test gets turned into a list (many values).
sub setMyData
{
my $objekt = shift;
my %myData = #_;
$objekt->{MYDATA} = \%myData;
}
However, there are a few more issues with your code.

Trouble passing hash and variable to subroutine

I want to pass a hash and a variable to a subroutine:
%HoA = {'1'=>'2'};
my $group_size = 10;
&delete_unwanted(\%HoA,$group_size);
sub delete_unwanted {
my (%HoA,$group_size) = #_;
print "'$group_size'\n"
}
But, this prints nothing.
You're passing a hash reference (as you should), so therefore assign it to a scalar in your parameter catching:
sub delete_unwanted {
my ($hashref, $group_size) = #_;
print "'$group_size'\n"
}
If you later want to dereference it, you can my %newHoA = %$hashref;, but that will be a copy of the original hash. To access the original structure, just use the reference: print $hashref->{a_key};.
Your problem is in:
my (%HoA,$group_size) = #_;
You can solve it by saying, for example:
sub delete_unwanted {
my $hashPointer = shift;
my $group_size = shift
Note that you can retrieve the original hash inside the subroutine by either: de-referencing the hashPointer (my %HoA = %$hashPointer), or you can access the hash contents directly using the pointer directly (eg, $hashPointer->{'key'})

Reference found where even-sized list expected in Perl - Possible pass-by-reference error?

I have a Perl class/module that I created to display Bible verses. In it there is a hash that stores several verses, with the key being the book/chapter/verse and the value being the text. This hash is returned from the module.
I'm including the Bible class in a controller class, and that connection seems to work. The problem is I keep getting errors on executing. My IDE because I'm following a Lynda tutorial, is Eclipse with the EPIC plugin.
The error is:
Reference found where even-sized list expected at C:/Documents and Settings/nunya/eric.hepperle_codebase/lynda/lamp/perl5/Exercise Files/14 Modules/eh_bibleInspiration_controller.pl line 42.
Use of uninitialized value $value in concatenation (.) or string at C:/Documents and Settings/nunya/eric.hepperle_codebase/lynda/lamp/perl5/Exercise Files/14 Modules/eh_bibleInspiration_controller.pl line 45.
HASH(0x19ad454) =>
Here is the CONTROLLER class:
#!/usr/bin/perl
# eh_bibleInspiration_controller.pl by Eric Hepperle - 06/23/13
#
use strict;
use warnings;
use Data::Dumper;
use EHW_BibleInspiration;
main(#ARGV);
sub main
{
my $o = EHW_BibleInspiration->new; # instantiate new object.
my %bo_ref = $o->getBibleObj();
print "\$o is type: " . ref($o) . ".\n";
print "\%bo_ref is type: " . ref(\%bo_ref) . ".\n";
# exit;
$o->getVerseObj();
listHash(\%bo_ref);
message("Done.");
}
sub message
{
my $m = shift or return;
print("$m\n");
}
sub error
{
my $e = shift || 'unkown error';
print("$0: $e\n");
exit 0;
}
sub listHash
{
my %hash = #_;
foreach my $key (sort keys %hash) {
my $value = $hash{$key};
message("$key => $value\n");
}
}
Here is the class that returns the verses and has the method to pick a random verse:
# EHW_BibleInspiration.pm
# EHW_BibleInspiration.
#
package EHW_BibleInspiration;
use strict;
use warnings;
use IO::File;
use Data::Dumper;
our $VERSION = "0.1";
sub new
{
my $class = shift;
my $self = {};
bless($self, $class); # turns hash into object
return $self;
}
sub getVerseObj
{
my ($self) = #_;
print "My Bible Verse:\n";
my $verses = $self->getBibleObj();
# get random verse
#$knockknocks{(keys %knockknocks)[rand keys %knockknocks]};
# sub mysub {
# my $params = shift;
# my %paramhash = %$params;
# }
# my %verses = %{$verses};
# my $random_value = %verses{(keys %verses)[rand keys %verses]};
# print Dumper(%{$random_value});
}
sub getBibleObj
{
my ($self) = #_;
# create bible verse object (ESV)
my $bibleObj_ref = {
'john 3:16' => 'For God so loved the world,that he gave his only Son, that whoever believes in him should not perish but have eternal life.',
'matt 10:8' => 'Heal the sick, raise the dead, cleanse lepers, cast out demons. You received without paying; give without pay.',
'Luke 6:38' => 'Give, and it will be given to you. Good measure, pressed down, shaken together, running over, will be put into your lap. For with the measure you use it will be measured back to you.',
'John 16:24' => 'Until now you have asked nothing in my name. Ask, and you will receive, that your joy may be full.',
'Psalms 32:7' => 'You are a hiding place for me; you preserve me from trouble; you surround me with shouts of deliverance. Selah',
'Proverbs 3:5-6' => 'Trust in the LORD with all your heart, and do not lean on your own understanding. 6 In all your ways acknowledge him, and he will make straight your paths.',
'John 14:1' => 'Let not your hearts be troubled. Believe in God; believe also in me.'
};
my $out = "The BIBLE is awesome!\n";
return $bibleObj_ref;
}
1;
What am I doing wrong? I suspect it has something to do with hash vs hash reference, but I don't know how to fix it. My dereferencing attempts had failed miserably because I don't really know what I'm doing. I modeled my random getter off of something I saw on perlmonks. #$knockknocks{(keys %knockknocks)[rand keys %knockknocks]};
In the main, you have:
my %bo_ref = $o->getBibleObj();
but, in package EHW_BibleInspiration;, the method getBibleObj returns : return $bibleObj_ref;
You'd do, in the main : my $bo_ref = $o->getBibleObj();
and then call listHash($bo_ref);
Finaly, don't forget to change sub listHash to:
sub listHash
{
my ($hash) = #_;
foreach my $key (sort keys %{$hash}) {
my $value = $hash->{$key};
message("$key => $value\n");
}
}
In your main, you do
listHash(\%bo_ref);
This passes a hash reference to the sub. However, it tries to unpack its arguments like
my %hash = #_;
Oops, it wants a hash.
Either, we pass it a hash: listHash(%bo_ref). This saves us much typing.
Or, we handle the reference inside the sub, like
sub listHash {
my ($hashref) = #_;
foreach my $key (sort keys %$hashref) {
my $value = $hashref->{$key};
print "$key => $value\n";
}
}
Notice how the reference uses the dereference arrow -> to access hash entries, and how it is dereferenced to a hash for keys.
I suspect that your suspicion is correct. In your method sub listHash you're correctly passing in the hash but you're trying to use a hash instead of a hash reference for the internal variable. Try using my ($hash) = #_; instead of my %hash = #_;.
When using references you can use the -> operator to de-reference it to get to the underlying values. The rest of your method should look like this:
sub listHash
{
my ($hash) = #_;
foreach my $key (sort keys %{$hash}) {
my $value = $hash->{$key};
message("$key => $value\n");
}
}
On line 43 of your program I had to tell Perl that the reference should be a hash reference by calling keys %{$hash}. Then on line 44 I de-referenced the hash to get the correct value by calling $hash->{$key}. For more information on Perl and references you can read the through the tutorial.
listHash is expecting a hash and you're passing it a hash reference. Change:
listHash(\%bo_ref);
to:
listHash(%bo_ref);

Why does Perl's strict not let me pass a parameter hash?

I hava a perl subroutine where i would like to pass parameters as a hash
(the aim is to include a css depending on the parameter 'iconsize').
I am using the call:
get_function_bar_begin('iconsize' => '32');
for the subroutine get_function_bar_begin:
use strict;
...
sub get_function_bar_begin
{
my $self = shift;
my %template_params = %{ shift || {} };
return $self->render_template('global/bars /tmpl_incl_function_bar_begin.html',%template_params);
}
Why does this yield the error message:
Error executing run mode 'start': undef error - Can't use string ("iconsize") as a HASH ref while "strict refs" in use at CheckBar.pm at line 334
Am i doing something wrong here?
Is there an other way to submit my data ('iconsize') as a hash?
(i am still new to Perl)
EDIT: Solution which worked for me. I didn't change the call, but my function:
sub get_function_bar_begin
{
my $self = shift;
my $paramref = shift;
my %params = (ref($paramref) eq 'HASH') ? %$paramref : ();
my $iconsize = $params{'iconsize'} || '';
return $self->render_template('global/bars/tmpl_incl_function_bar_begin.html',
{
'iconsize' => $iconsize,
}
);
}
You are using the hash-dereferencing operator ( %{ } ) on the first argument of your parameter list. But that argument is not a hash reference, it's just the string 'iconsize'. You can do what you want by one of two ways:
Pass an anonymous hash reference:
get_function_bar_begin( { 'iconsize' => '32' } );
Or continue to pass a normal list, as you are right now, and change your function accordingly:
sub get_function_bar_begin {
my $self = shift;
my %template_params = #_;
}
Notice in this version that we simply assign the argument list directly to the hash (after extracting $self). This works because a list of name => value pairs is just syntactic sugar for a normal list.
I prefer the second method, since there's no particularly good reason to construct an anonymous hashref and then dereference it right away.
There's also some good information on how this works in this post: Object-Oriented Perl constructor syntax.
You're violating strict refs by trying to use the string iconsize as a hash reference.
I think you just want:
my( $self, %template_params ) = #_;
The first argument will go into $self and the rest create the hash by taking pairs of items from the rest of #_.
Passing hash with parameters as list
You need to use #_ variable instead of shift. Like this:
my %template_params = #_; ## convert key => value pairs into hash
There is different between hashes and references to hash in perl. Then you pass 'iconsize' => '32' as parameter this means list to perl, which can be interpreited as hash.
Passing hash with parameters as hash reference
But when you try %{ shift || {} } perl expect second parameter to be a hash references. In this case you can fix it in following way:
get_function_bar_begin({ 'iconsize' => '32' }); ## make anonymous hash for params
The problem is this line:
get_function_bar_begin('iconsize' => '32');
This does not pass a hash reference, as you seem to think, but a hash, which appears as a list to the callee. So when you do %{ shift }, you're only shifting the key 'iconsize', not the entire list. The solution is actually to make the second line of your function simpler:
my %template_params = #_;