How to access hash from object - perl

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.

Related

Accessing and displaying key-value pairs in Perl from subroutine

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};
}

Perl, shift if(#_)

Can someone tell me what
shift if(#_)
means in perl?
and so what would
sub id {
my $self = shift;
$self->{ID} = shift if #_;
return $self->{ID};
}
mean? Thanks.
That is a handrolled accessor/mutator for an object
print $obj->id; # Accessor form
$obj->id(NEW_VAL); # Mutator form
It is functionally equivalent to:
sub id {
my $self = shift;
if (#_) { # If called with additional parameters, set the value:
$self->{ID} = shift(#_);
}
return $self->{ID};
}
In a sub, shift without argument uses #_ array.
So
$self->{ID} = shift if #_;
is equal to
$self->{ID} = shift(#_) if #_;
(remove leftmost element from #_ array and assign it to $self->{ID})
shift takes the first element off an array and returns it. If no array is given, it operates on #_, the array containing function arguments. The if #_ statement modifier causes the preceding statement to be executed only if #_ has at least one element.
In your example, $self->{ID} = shift #_ if #_; means "If there is a function argument, assign it to $self->{ID}.
shift if(#_) says "if #_ has any elements (i.e., evaluating #_ in scalar context is greater than zero), shift off the first element of the default argument (inside a subroutine, this is #_)".
The sub is a standard pre-Moose setter/getter method. Commented to explain:
sub id {
my $self = shift; # #_ is the implied argument of shift.
# Since method calls prepend the object reference to #_,
# this grabs the object itself, assumed by the later code
# to be a hash reference.
$self->{ID} = shift if #_;
# If there's still anything left in #_ (`if #_`), get
# the first item and stash it under the ID key in
# the hash referenced by $self (note that if there is more
# than one item, we'll only stash the first one).
return $self->{ID}; # Return whatever the value of the item stored under ID in the
# hash referenced by $self is. This will be the value just
# assigned if the method was called with a scalar argument,
# or whatever value was there before if no argument was passed.
# This will be undef if nothing was ever stored under the ID key.
}
Invocation would be
$obj->id();
to fetch, and
$obj->id($value);
to set.
A standard pre-Moose constructor would create an anonymous hash reference, bless it to turn it into an object (connecting a package implementing the class's behavior to that reference), and return it:
sub new {
my($class) = #_; # List assignment to a list; puts first item in one into the first
# item in another; a call to a class method prepends the package
# (class) name to #_, so this gets us the name of the class this
# object is to belong to.
my $self = {}; # Gets a new anonymous hash reference into $self.
bless $self, $class; # Connects the hash reference to the package, so that method calls
# made on this object are directed to this class (and its #ISA
# ancestors) to find the sub to be called to implement the method.
return $self; # Hands the object back to the caller.
}
There is no shift if #_ in your example -- if applies to the entire statement, not just the shift expression (because if has very low precedence in the expression hierarchy).
$self->{ID} = shift if #_;
is short for:
if (#_) {
$self->{ID} = shift;
}
shift with no argument pulls the first element off the #_ list, so this sets $self->{ID} to the first argument of the subroutine if there are any arguments.
The general rule regarding postfix use of if is that:
<expression1> if <expression2>;
is short for:
if (<expression2>) {
<expression1>;
}

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'})

Passing variables through functions

When I pass a variable through a couple of subs, it always turns up empty. Why is this ?
sub Main {
my $myVariable = "Test string";
firstSub($myVariable);
}
sub firstSub {
my($myVariable) = #_;
my #array = `some command`;
secondSub(#array, $myVariable);
}
sub secondSub {
my(#array, $myVariable) = #_;
print $myVariable;
}
echo will be undef.
echo is not a valid Perl function. You're confusing shells with Perl here. Try "print" or "say" (the latter with Perl 5.10 and newer).
Also you cannot assign an array & a scalar variable to another array & scalar variable. Meaning this won't work because all of the elements of the right-hand side will be assigned to the array on left-hand side, and nothing will be assigned to the scalar: my (#array, $myVariable) = #_; Either swap the order of the elements my ($myVariable, #array) = #_; (also when calling the function) or use array references instead of full arrays.
Your code doesn't do anything because you have defined three subroutines, but you have never called them.
Just add Main(); to actually run your main sub.
Also, you need print instead of echo.
Also, the passing of variables is incorrect, as Moritz Bunkus explained.
When you call secondsub() the #array and the $myVariable is being sent as a list(a single element) and is been assigned to #array in the secondsub function. You can see both the #array and $myVariable values when you print #array in secondsub.
You have to pass the array as a reference and receive it as a scalar value in secondsub. The below code will work.
&Main();
sub Main {
my $myVariable = "Test string";
firstSub($myVariable);
}
sub firstSub {
my($myVariable) = #_;
my #array = `some command`;
secondSub(\#array,$myVariable);
}
sub secondSub {
my($ref,$myVariable) = #_;
print $myVariable;
}
Passing varibles:
my $txt = "this text for sample";
Function_Passing_varible($txt);
sub Function_Passing_varible{
my $text = shift;
print $text;
}
I think you like this answer......

Perl woes - assigning and returning a hashes

I have an instance variable, properties, that is being declared and instantiated like so:
$self->{properties}{$key1} = $value;
My understanding that this will declare the properties field, and also set it to a Hash primitive, containing one key value pair.
I'm trying to write a getter for the properties instance variable, that will return the hash:
sub getProperties{
my $self = shift;
my %myhash = $self->{properties};
return %myhash;
}
And subsequently call the getter like so:
my %properties = $properties->getProperties();
When I try to compile this i get:
"Odd number of elements in hash assignment at 70..."
line 70 being: my %myhash = $self->{properties};
In this line of code:
my %myhash = $self->{properties};
%myhash is a hash whereas $self->{properties} is a hash reference. So you're effectively returning a hash with one key/value pair where the key is a reference to a hash and the value is undef.
If you really want to return a hash, do this:
my %myhash = %{$self->{properties}};
Alternatively, return a hash reference. That's generally preferable to returning a hash, since it doesn't make a copy of the original hash and therefore is more memory efficient as the hash gets larger. Here's how it looks:
sub getProperties {
my $self = shift;
return $self->{properties};
}
Then in your calling code instead of this:
my %properties = $properties->getProperties();
$somevalue = $properties{'somekey'};
do this:
# getProperties returns a reference, so assign to a scalar
# variable ($foo) rather than a hash (%foo)
my $properties = $properties->getProperties();
# Use -> notation to dereference the hash reference
$somevalue = $properties->{'somekey'};
isn't $self->{properties} a hashref not a hash?
$ perl t4.pl
size -> 42
t4.pl
#!/usr/bin.perl
use strict;
use warnings;
use t4;
my $t4 = t4->new();
my %hash = $t4->getProperties();
for my $key (keys %hash) {
print "$key -> $hash{$key}\n";
}
t4.pm
package t4;
sub new {
my $class = shift;
my $self = {};
$self->{properties}{size} = 42;
bless ($self, $class);
}
sub getProperties {
my $self = shift;
my %myhash = %{$self->{properties}};
return %myhash;
}
1;