I have a hash which I am passing to a subroutine. I pass an empty hash to subroutine as well. I want to copy the contents of the hash to the empty one.
Here's how my code looks:
my %emptyHash = ();
my %sourceHash = ();
$sourceHash{'apple'} = 'red';
$sourceHash{'orange'} = 'orange';
mergeHash(\%emptyHash, \%sourceHash);
foreach my $k (keys %emptyHash)
{
print "key=>". $k. "value=>".$emptyHash{$k}. "\n";
}
sub mergeHash {
my $emptyHash = shift;
my $sourceHash = shift;
$emptyHash = $sourceHash;
}
When I print the emptyHash after the merge, I still get an empty hash. Can I not just copy the value of one hash to another this way?
$emptyhash and $sourcehash are references. When you do $emptyhash = $sourcehash you just make the variable $emptyhash contain the same reference that $sourcehash does.
Instead, you want %$emptyhash = %$sourcehash, which acts on the hashes those references point to.
Do you want a quick copy or a deep one? A quick copy copies only the top-level scalars of a hash. If the hash contains anonymous arrays or hashes, they will not be copied. Instead, a reference to them is made. That means any changes inside of them will cause the same changes in the copied hash.
Quick copy:
%$empty_hash = %$source_hash;
Deep copy:
use Storable qw( dclone );
$empty_hash = declone( $source_hash );
Storable is a standard module that comes with Perl. For a list of the standard modules, see perldoc perlmodlib.
I fixed your code below.
my %emptyHash = ();
my %sourceHash = ();
$sourceHash{'apple'} = 'red';
$sourceHash{'orange'} = 'orange';
my $d = mergeHash(\%emptyHash, \%sourceHash);
foreach my $k (keys %$d)
{
print "key=>". $k. "\tvalue=>". $emptyHash{$k}. "\n";
}
sub mergeHash {
my $emptyHash = shift;
my $sourceHash = shift;
%$emptyHash = %$sourceHash;
return $emptyHash;
}
Related
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.
I'm trying to call a procedure, which is filling a hash by reference. The reference to the hash is given as a parameter. The procedure fills the hash, but when I return, the hash is empty. Please see the code below.
What is wrong?
$hash_ref;
genHash ($hash_ref);
#hash is empty
sub genHash {
my ($hash_ref)=(#_);
#cut details; filling hash in a loop like this:
$hash_ref->{$lid} = $sid;
#hash is generetad , filled and i can dump it
}
You might want to initialize hashref first,
my $hash_ref = {};
as autovivification happens inside function to another lexical variable.
(Not so good) alternative is to use scalars inside #_ array which are directly aliased to original variables,
$_[0]{$lid} = $sid;
And btw, consider use strict; use warnings; to all your scripts.
The caller's $hash_ref is undefined. The $hash_ref in the sub is therefore undefined too. $hash_ref->{$lid} = $sid; autovivifies the sub's $hash_ref, but nothing assigns that hash reference to the caller's $hash_ref.
Solution 1: Actually passing in a hash ref to assign to the caller's $hash_ref.
sub genHash {
my ($hash_ref) = #_;
...
}
my $hash_ref = {};
genHash($hash_ref);
Solution 2: Taking advantage of the fact that Perl passes by reference.
sub genHash {
my $hash_ref = $_[0] ||= {};
...
}
my $hash_ref;
genHash($hash_ref);
-or-
genHash(my $hash_ref);
Solution 3: If the hash is going to be empty initially, why not just create it in the sub?
sub genHash {
my %hash;
...
return \%hash;
}
my $hash_ref = genHash();
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'})
I would like to pass the following variables to subroutine mySubroutine, $name, $age and then this multidimensional array:
$name = "jennifer";
$age = 100;
$list[0][0] = "TEST NAME 2";
$list[0][1] = "TEST GROUP 2";
$[0][2] = 10;
$[1][0] = "TEST NAME 2";
$[1][1] = "TEST GROUP 2";
$[1][2] = 2;
Subroutine:
sub mySubroutine
{
}
I have tried $_[0], and #_, but I don't seem to get the variables passed to the subroutine correctly.
There are several ways to do it (like most things in Perl). I personally do it like this:
sub mySubroutine
{
# Get passed arguments
my ($name, $age, $refList) = #_;
# Get the array from the reference
my #list = #{$refList};
# Good to go
}
# You need to pass #list as reference, so you
# put \#list, which is the reference to the array
mySubroutine($name, $age, \#list);
Another option, as long as you are only passing one array, is to pass it normally by value as the last element:
sub scalars_and_one_array {
my $name = shift;
my $age = shift;
foreach my $element (#_)
{
# Do something with the array that was passed in.
}
}
scalars_and_one_array($name,$age,#array);
However, it is most efficient to avoid any additional copy of the array by only using a reference within the sub. This does mean that changes to the array in the sub affect the original, however:
sub array_by_ref {
my $array_ref = shift;
foreach my $element (#$array_ref)
{
# Changing $element changes #original_array!
}
}
array_by_ref(\#original_array);
Another way, which passes the array by reference, but then makes a copy of it to avoid changing the original when you edit it.
sub mySubroutine
{
## Retrieve name
my $name = shift;
## Retrieve age
my $age = shift;
## Retrieve list reference
my $refList = shift;
## De-reference the list's scalar
my #list = #{$refList};
# Good to go
}
## Function call
mySubroutine($name, $age, \#list);
For a better understanding, please refer to perlsub (Perl subroutines).
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;