Binding two Perl hashes (not two anonymous hashes) - perl

In order to reuse some already written code libraries, I need to "masquerade" a hash (%bar) into another hash (%foo) so that each changes made in %bar can be seen with %foo after their binding.
With an anonymous hash construct, it's easy: just copy the ref of bar into the ref of foo ( $foo = $bar; ).
How about "not using" an anonymous hash (because the code I have to reuse is built not using anonymous hash constructs) ? Is this only possible ?
Thanks.
use strict;
use Data::Dumper;
my %foo = ();
my %bar = ();
%foo = %bar; # This won't work as it copies %bar into %foo as at the current time.
$bar{A}->{st} = 'a';
$bar{A}->{qt} = 'a';
$bar{B}->{st} = 'b';
$bar{B}->{qt} = 'b';
# $foo{A}->{st} doesn't exist
Of course, an anonymous hash construct would have been a blessing.
use strict;
use Data::Dumper;
my $foo = {};
my $bar = {};
$foo = $bar; # This works fine.
$bar->{A}->{st} = 'a';
$bar->{A}->{qt} = 'a';
$bar->{B}->{st} = 'b';
$bar->{B}->{qt} = 'b';
print STDOUT "foo\n---\n";
print Dumper($foo) . "\n\n";
print STDOUT "bar\n---\n";
print Dumper($bar) . "\n\n";
Yields the expected result:
foo
---
$VAR1 = {
'B' => {
'st' => 'b',
'qt' => 'b'
},
'A' => {
'qt' => 'a',
'st' => 'a'
}
};
bar
---
$VAR1 = {
'B' => {
'st' => 'b',
'qt' => 'b'
},
'A' => {
'qt' => 'a',
'st' => 'a'
}
};

For your example, you can use Data::Alias to alias %bar to %foo. That allows you to make changes in %bar and later see those in %foo.
use strict;
use warnings;
use Data::Printer;
use Data::Alias;
my %foo = ();
my %bar = ();
alias %foo = %bar;
$bar{A}->{st} = 'a';
$bar{A}->{qt} = 'a';
$bar{B}->{st} = 'b';
$bar{B}->{qt} = 'b';
p %foo;
p %bar;
Output:
{
A {
qt "a",
st "a"
},
B {
qt "b",
st "b"
}
}
{
A {
qt "a",
st "a"
},
B {
qt "b",
st "b"
}
}

Related

How to remove duplicate reference in array?

Consider this array:
my #array = (hashref1, hashref2, hashref3, hashref1);
How can I remove the duplicate reference hashref1.
If by "duplicate reference" you mean they both refer to the same anonymous hash, you can use List::Util's uniq:
#!/usr/bin/perl
use warnings;
use strict;
use List::Util qw{ uniq };
use Data::Dumper;
my $hashref1 = {a => 10};
my $hashref2 = {b => 11};
my $hashref3 = {c => 12};
my #array = ($hashref1, $hashref2, $hashref3, $hashref1);
print Dumper(uniq(#array));
Output:
$VAR1 = {
'a' => 10
};
$VAR2 = {
'b' => 11
};
$VAR3 = {
'c' => 12
};
The stringified versions of the references will be used for comparison, i.e. something like HASH(0x560ee5fdf220). The same references have the same address.
But, if you mean they refer to different objects with the same contents, you need to find a way how to stringify the hashrefs so that the contents is always the same.
#!/usr/bin/perl
use warnings;
use strict;
use List::Util qw{ uniq };
use Data::Dumper;
my $hashref1 = {a => 10, A => 2};
my $hashref2 = {b => 11, B => 3};
my $hashref3 = {c => 12, C => 4};
my $hashref4 = {a => 10, A => 2};
my #array = ($hashref1, $hashref2, $hashref3, $hashref4);
my #serialized = do {
# Comment out the following line to see how 1 and 4
# can be different sometimes.
local $Data::Dumper::Sortkeys = 1;
map Dumper($_), #array;
};
my $VAR1;
print Dumper(map eval, uniq(#serialized));

In perl, can I dynamically create variables within a subroutine?

Background
In the code I'm writing, I'm passing data into methods using a hash-ref (see note [1]).
This, unfortunately, leads to a lot of repetitive code:
sub thing {
my ($self, $params) = #_;
my ($foo, $bar, $baz, $biff,);
if ( exists $params->{foo} && $params->{foo} ) {
$foo = $params->{foo};
}
# repeat for `bar`, `baz`, `biff`
## rest of function ##
}
(and duplicate in every function with parameters)
What I want to do
What would be far easier is to define a list of parameters, and then
iterate of that list, creating both the variables and setting them to a value if needed.
So to test this, I tried:
my $params = { x => 1, y => 2};
my #params = qw(x y z a b c);
gno strict 'refs';
rep( ${$_}, #params );
use strict 'refs';
foreach my $p (#params) {
if ( exists $params->{$p} && $params->{$p} ) {
${$p} = $params->{$p};
}
}
print "x:$x, y:$y, z:$z, a:$a, b:$b, c:$c\n"
which gives me the following error:
Global symbol "$x" requires explicit package name at ./test.pl line 20.
Global symbol "$y" requires explicit package name at ./test.pl line 20.
Global symbol "$z" requires explicit package name at ./test.pl line 20.
Global symbol "$c" requires explicit package name at ./test.pl line 20.
Can I do this dynamic variable creation thing? (and if so, how?)
[1] By using a hash to pass data in, I gain in many ways:
There is a clear indication of What each item of data is
The ORDER of the pieces of data is no longer important
I can miss one or more pieces of data, and I don't need to add in random undef values
I'm passing less data: 1 scalar (a reference) rather than multiple scalars
(I accept the danger of functions being able to change the parent's data, rather that mucking around with a copy of it...)
Yes, you can do this in Perl. But it's a terrible idea for all of the reasons explained by Mark Dominus in these three articles.
It's a far better idea to store these values in a hash.
#!/usr/bin/perl
use strict;
use warnings;
my $params = { x => 1, y => 2};
my #params = qw(x y z a b c);
my %var;
foreach my $p (#params) {
# You need to take care exactly what you want in this
# logical statement. The options are:
# 1/ $p exists in the hash
# exists $params->{$p}
# 2/ $p exists in the hash and has a defined value
# defined $params->{$p}
# 3/ $p exists in the hash and has a true value
# $params->{$p}
# I think the first option is most likely. The last one has
# good chance of introducing subtle bugs.
if ( exists $params->{$p} ) {
$var{$p} = $params->{$p};
}
}
print join ', ', map { "$_: " . ($var{$_} // 'undef') } #params;
print "\n";
It's a really bad idea to use symbolic references like this... hashes pretty well completely eliminate the need for this.
use warnings;
use strict;
my $params = { x => 1, y => 2, foo => 3, };
thing($params);
sub thing {
my $params = shift;
my $foo;
if (defined $params->{foo}){
$foo = $params->{foo};
}
print $foo;
}
You can also pass in a hash itself directly (whether it be pre-created, or passed inline to the sub. If pre-created, the sub will operate on a copy).
thing(foo => 1, x => 2);
sub thing {
my %params = #_;
print $params{foo} if defined $params{foo};
}
With thanks to Dave Cross & others - the following test works:
#!/usr/bin/perl
use strict;
use warnings;
use English qw( -no_match_vars ) ;
use Carp;
use Data::Dumper;
my $params = { x => 1, y => 2, z => 0};
my #params = qw(x y z a b c);
my %var;
foreach my $p (#params) {
if ( exists $params->{$p} ) {
$var{$p} = $params->{$p};
} else {
$var{$p} = undef;
}
}
print Dumper \%var;
This gives me %var with all desired parameters (as listed in #params, with the ones that are not passed in (ie, not in the $params hashref) created with an undef value.
Thus I can confidently test for value and truth, without worrying about existence.
Thank you all.
I did this using soft references:
#!perl
no strict "refs";
my %vars = ( x => 1, y => 2 );
for my $k ( keys %vars ) {
$$k = $vars{$k};
}
print $x, $y;
But there's a reason why the recommended settings (use strict; use warnings;) prevent this kind of pattern. It is easy to shoot yourself in the foot with it.
perl -Mstrict -MData::Dumper -wE'
{package Data::Dumper;our($Indent,$Sortkeys,$Terse,$Useqq)=(1)x4}
my #aok = qw( x y z a b c );
my %dft = ( a => -1 );
say "- - - -";
my $arg = { x => 1, y => 2, foo => 42 };
$arg = { %dft, %$arg };
say "arg: ", Dumper($arg);
my %var;
#var{ #aok } = #$arg{ #aok };
say "var: ", Dumper(\%var);
my %aok = map { $_ => 1 } #aok;
my #huh = grep !$aok{$_}, sort keys %$arg;
#huh and say "huh: ", Dumper(\#huh);
'
- - - -
arg: {
"a" => -1,
"foo" => 42,
"x" => 1,
"y" => 2
}
var: {
"a" => -1,
"b" => undef,
"c" => undef,
"x" => 1,
"y" => 2,
"z" => undef
}
huh: [
"foo"
]

Adding hash keys

I am adding data to a hash using an incrementing numeric key starting at 0. The key/value is fine. When I add the second one, the first key/value pair points back to the second. Each addition after that replaces the value of the second key and then points back to it. The Dumper output would be something like this.
$VAR1 = { '0' => { ... } };
After the first key/value is added. After the second one is added I get
$VAR1= { '1' => { ... }, '0' => $VAR1->{'1} };
After the third key/value is added, it looks like this.
$VAR1 = { '1' => { ... }, '0' => $VAR1->{'1'}, '2' => $VAR1->{'1'} };
My question is why is it doing this? I want each key/value to show up in the hash. When I iterate through the hash I get the same data for every key/value. How do I get rid of the reference pointers to the second added key?
You are setting the value of every element to a reference to the same hash. Data::Dumper is merely reflecting that.
If you're using Data::Dumper as a serializing tool (yuck!), then you should set $Data::Dumper::Purity to 1 to get something eval can process.
use Data::Dumper qw( Dumper );
my %h2 = (a=>5,b=>6,c=>7);
my %h;
$h{0} = \%h2;
$h{1} = \%h2;
$h{2} = \%h2;
print("$h{0}{c} $h{2}{c}\n");
$h{0}{c} = 9;
print("$h{0}{c} $h{2}{c}\n");
{
local $Data::Dumper::Purity = 1;
print(Dumper(\%h));
}
Output:
7 7
9 9
$VAR1 = {
'0' => {
'c' => 9,
'a' => 5,
'b' => 6
},
'1' => {},
'2' => {}
};
$VAR1->{'0'} = $VAR1->{'1'};
$VAR1->{'2'} = $VAR1->{'1'};
If, on the other hand, you didn't mean to use store references to different hashes, you could use
# Shallow copies
$h{0} = { %h2 }; # { ... } means do { my %anon = ( ... ); \%anon }
$h{1} = { %h2 };
$h{2} = { %h2 };
or
# Deep copies
use Storable qw( dclone );
$h{0} = dclone(\%h2);
$h{1} = dclone(\%h2);
$h{2} = dclone(\%h2);
Output:
7 7
9 7
$VAR1 = {
'0' => {
'a' => 5,
'b' => 6,
'c' => 9
},
'1' => {
'a' => 5,
'b' => 6,
'c' => 7
},
'2' => {
'a' => 5,
'b' => 6,
'c' => 7
}
};
You haven't posted the actual code you're using to build the hash, but I assume it looks something like this:
foreach my $i (1 .. 3) {
%hash2 = (number => $i, foo => "bar", baz => "whatever");
$hash1{$i} = \%hash2;
}
(Actually, I'll guess that, in your actual code, you're probably reading data from a file in a while (<>) loop and assigning values to %hash2 based on it, but the foreach loop will do for demonstration purposes.)
If you run the code above and dump the resulting %hash1 using Data::Dumper, you'll get the output:
$VAR1 = {
'1' => {
'baz' => 'whatever',
'number' => 3,
'foo' => 'bar'
},
'3' => $VAR1->{'1'},
'2' => $VAR1->{'1'}
};
Why does it look like that? Well, it's because the values in %hash1 are all references pointing to the same hash, namely %hash2. When you assign new values to %hash2 in your loop, those values will overwrite the old values in %hash2, but it will still be the same hash. Data::Dumper is just highlighting that fact.
So, how can you fix it? Well, there are (at least) two ways. One way is to replace \%hash2, which gives a reference to %hash2, with { %hash2 }, which copies the contents of %hash2 into a new anonymous hash and returns a reference to that:
foreach my $i (1 .. 3) {
%hash2 = (number => $i, foo => "bar", baz => "whatever");
$hash1{$i} = { %hash2 };
}
The other (IMO preferable) way is to declare %hash2 as a (lexically scoped) local variable within the loop using my:
foreach my $i (1 .. 3) {
my %hash2 = (number => $i, foo => "bar", baz => "whatever");
$hash1{$i} = \%hash2;
}
This way, each iteration of the loop will create a new, different hash named %hash2, while the hashes created on previous iterations will continue to exist (since they're referenced from %hash1) independently.
By the way, you wouldn't have had this problem in the first place if you'd followed standard Perl best practices, specifically:
Always use strict; (and use warnings;). This would've forced you to declare %hash2 with my (although it wouldn't have forced you to do so inside the loop).
Always declare local variables in the smallest possible scope. In this case, since %hash2 is only used within the loop, you should've declared it inside the loop, like above.
Following these best practices, the example code above would look like this:
use strict;
use warnings;
use Data::Dumper qw(Dumper);
my %hash1;
foreach my $i (1 .. 3) {
my %hash2 = (number => $i, foo => "bar", baz => "whatever");
$hash1{$i} = \%hash2;
}
print Dumper(\%hash1);
which, as expected, will print:
$VAR1 = {
'1' => {
'baz' => 'whatever',
'number' => 1,
'foo' => 'bar'
},
'3' => {
'baz' => 'whatever',
'number' => 3,
'foo' => 'bar'
},
'2' => {
'baz' => 'whatever',
'number' => 2,
'foo' => 'bar'
}
};
It's hard to see what the problem is when you don't post the code or the actual results of Data::Dumper.
There is one thing you should know about Data::Dumper: When you dump an array or (especially) a hash, you should dump a reference to it. Otherwise, Data::Dumper will treat it like a series of variables. Also notice that hashes do not remain in the order you create them. I've enclosed an example below. Make sure that your issue isn't related to a confusing Data::Dumper output.
Another question: If you're keying your hash by sequential keys, would you be better off with an array?
If you can, please edit your question to post your code and the ACTUAL results.
use strict;
use warnings;
use autodie;
use feature qw(say);
use Data::Dumper;
my #array = qw(one two three four five);
my %hash = (one => 1, two => 2, three => 3, four => 4);
say "Dumped Array: " . Dumper #array;
say "Dumped Hash: " . Dumper %hash;
say "Dumped Array Reference: " . Dumper \#array;
say "Dumped Hash Reference: " . Dumper \%hash;
The output:
Dumped Array: $VAR1 = 'one';
$VAR2 = 'two';
$VAR3 = 'three';
$VAR4 = 'four';
$VAR5 = 'five';
Dumped Hash: $VAR1 = 'three';
$VAR2 = 3;
$VAR3 = 'one';
$VAR4 = 1;
$VAR5 = 'two';
$VAR6 = 2;
$VAR7 = 'four';
$VAR8 = 4;
Dumped Array Reference: $VAR1 = [
'one',
'two',
'three',
'four',
'five'
];
Dumped Hash Reference: $VAR1 = {
'three' => 3,
'one' => 1,
'two' => 2,
'four' => 4
};
The reason it is doing this is you are giving it the same reference to the same hash.
Presumably in a loop construct.
Here is a simple program which has this behaviour.
use strict;
use warnings;
# always use the above two lines until you
# understand completely why they are recommended
use Data::Printer;
my %hash;
my %inner; # <-- wrong place to put it
for my $index (0..5){
$inner{int rand} = $index; # <- doesn't matter
$hash{$index} = \%inner;
}
p %hash;
To fix it just make sure that you are creating a fresh hash reference every time through the loop.
use strict;
use warnings;
use Data::Printer;
my %hash;
for my $index (0..5){
my %inner; # <-- place the declaration here instead
$inner{int rand} = $index; # <- doesn't matter
$hash{$index} = \%inner;
}
p %hash;
If you are only going to use numbers for your indexes, and they are monotonically increasing starting from 0, then I would recommend using an array.
An array would be faster and more memory efficient.
use strict;
use warnings;
use Data::Printer;
my #array; # <--
for my $index (0..5){
my %inner;
$inner{int rand} = $index;
$array[$index] = \%inner; # <--
}
p #array;

Getting these loop errors in basic Perl

I have the following code in Perl. I am very new to the language:
#!/usr/bin/perl
use strict;
use warnings;
my $date = $ARGV[0];
my $symbols = ('A', 'B', 'C');
foreach $symbol (%symbols)
{
my $print = "$symbol";
print "$print";
}
Getting:
Useless use of a constant in void context at (line of %symbols)
and
Global symbol "$symbol requires explicit package name at ..."
and
Global symbol "%symbols" require explicit package. name at ..."
You are using an Hash when an Array is all that is needed.
#!/usr/bin/perl
use strict;
use warnings;
my $date = $ARGV[0];
my #symbols = ('A', 'B', 'C');
foreach my $symbol (#symbols)
{
print $symbol;
}
1) Your $symbols should be #symbols, since it's an array. Later in the foreach, %symbols should be #symbols.
2) The $symbol is not declared. Say foreach my $symbol... instead.
You are declaring $symbols instead of #symbols, so it is putting that in scalar context and setting it to 'C'. Then you try to loop through a hash with the same name, which you never created. Remember, $a (scalar), #a (array) and %a (hash) are all different.
This is what you wanted:
my #symbols = qw/ A B C /; ## the same as ( 'A', 'B', 'C' )
foreach my $symbol ( #symbols ) {
print $symbol;
}
Really quick:
my #symbols = qw/ A B C /; ## new array with three values
my $symbols = qw/ A B C /; ## new scalar that is the last element of the "A B C" list ($symbols = 'C')
my %symbols = (
A => 1,
B => 2,
C => 3,
); ## a hash with three key/value pairs
Your foreach is looking at each symbol in a non-existent hash called %symbols, not your array #symbols.
foreach $symbol (#symbols)
{
my $print = "$symbol";
print "$print";
}

How to create hash of hash key

I have the following code
use strict;
use warnings;
use Data::Dumper;
my $s = "12 A P1
23 B P5
24 C P2
15 D P1
06 E P5 ";
my $hash = {};
my #a = split(/\n/, $s);
foreach (#a)
{
my $c = (split)[2];
my $d = (split)[1];
my $e = (split)[0];
push(#{$hash->{$c}}, $d);
}
print Dumper($hash );
I am getting the output
$VAR1 = {
'P5' => [
'B',
'E'
],
'P2' => [
'C'
],
'P1' => [
'A',
'D'
]
};
But I want the output like
$VAR1 = {
'P5' => {
'E' => '06',
'B' => '23'
},
'P2' => {
'C' => '24'
},
'P1' => {
'A' => '12',
'D' => '15'
}
};
Please help.
You need to use a hash if you want a hash as output.
No need to split three times and use postscripts, just split once and assign all variables. Also no need to initialize a scalar as an empty hash, perl will take care of that for you.
I renamed the variables for increased readability.
my $string = "12 A P1
23 B P5
24 C P2
15 D P1
06 E P5 ";
my $hash;
my #lines = split(/\n/, $string);
foreach (#lines)
{
my ($value, $key2, $key) = split;
$hash->{$key}{$key2} = $value;
}
print Dumper($hash );
Be aware that if you have multiple values with the same keys, they will overwrite each other. In that case, you'd need to push the values onto an array instead:
push #{$hash->{$key}{$key2}}, $value;
Well it's not that different from what you have. Just replace the push with a hash-assign (thank you auto-vivification):
foreach (#a)
{
my ($e, $d, $c) = split;
$hash->{$c}->{$d} = $e;
}
Additionally I have re-arranged the "split" so that it's just called once per line.