Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Putting an array into a hash gives an unexpected reference to the hash itself.

by polarbear (Novice)
on Nov 07, 2012 at 23:05 UTC ( #1002765=perlquestion: print w/ replies, xml ) Need Help??
polarbear has asked for the wisdom of the Perl Monks concerning the following question:

I don't understand what is going on in the following code. Service1 comes out as I expect with an array of email addresses. I expected Service2 to look the same but instead it gets a reference to service1 which is completely puzzling to me. I want to be able to add more addresses to the email lists individually for each service later on. I don't want the lists connected in any way.

#!/usr/local/bin/perl -w use strict; use warnings; use Data::Dumper; my @maintainers = qw/ worker@company.com manager@company.com /; my %services = ( service1 => { email => \@maintainers }, service2 => { email => \@maintainers }, ); print Data::Dumper->Dump( [ \%services ], [qw/*services/] ); exit 0;
Output:
%services = ( 'service1' => { 'email' => [ 'worker@company.com', 'manager@company.com' ] }, 'service2' => { 'email' => $services{'service1'}{'email'} } );
UPDATE: Thanks for all the replys, they did solve my problem though I am still puzzled as to why I got a reference to the hash value 'email' => $services{'service1'}{'email'} rather than a reference to say 'email' => $maintainers[0];

Comment on Putting an array into a hash gives an unexpected reference to the hash itself.
Select or Download Code
Re: Putting an array into a hash gives an unexpected reference to the hash itself.
by frozenwithjoy (Curate) on Nov 07, 2012 at 23:26 UTC
    The problem is that you are creating two references to the same thing and want them to be distinct, but they aren't (because they reference the same thing...).

    Instead of this:

    my %services = ( service1 => { email => \@maintainers }, service2 => { email => \@maintainers }, );

    You probably want this:

    my %services = ( service1 => { email => [ @maintainers ] }, service2 => { email => [ @maintainers ] }, );

    Below is an example showing what can happen when using/altering references. I make two references to a single scalar, alter one of the references and show that the contents of the original scalar and both references to that scalar change (but not a copy of the original).

    #!/usr/bin/env perl use strict; use warnings; use feature 'say'; my $a = "original"; my $a_ref1 = \$a; my $a_ref2 = \$a; my $a_copy = $a; say "\$a is $a"; say "\$a_ref1 is $$a_ref1"; say "\$a_ref2 is $$a_ref2"; say "\$a_copy is $a_copy"; say "OK, let's alter only the first reference (\$a_ref1):"; $$a_ref1 = "altered"; say "\$a is $a"; say "\$a_ref1 is $$a_ref1"; say "\$a_ref2 is $$a_ref2"; say "\$a_copy is $a_copy";

    Output:

    $a is original $a_ref1 is original $a_ref2 is original $a_copy is original OK, let's alter the first reference ($a_ref1): $a is altered $a_ref1 is altered $a_ref2 is altered $a_copy is original
      Thank you, that solved my problem.
Re: Putting an array into a hash gives an unexpected reference to the hash itself.
by brx (Pilgrim) on Nov 07, 2012 at 23:27 UTC

    \@maintainers is ref to an array. You use the same ref twice. I think you want to use  [ @maintainers ] : a ref to an anonymous array with values of @maintainers.

    But, if  @maintainers contains some ref to other data, you will have the same problem, but one level deeper.

    English is not my mother tongue.
    Les tongues de ma mère sont "made in France".
Re: Putting an array into a hash gives an unexpected reference to the hash itself.
by Perlbotics (Abbot) on Nov 07, 2012 at 23:29 UTC

    Sure, they are the same. You assigned the same reference twice.

    If you want two different copies, use two different references. E.g. the original \@maintainers and a copy [ @maintainers ] or two new copies.

    #!/usr/local/bin/perl -w use strict; use warnings; use Data::Dumper; my @maintainers = qw/ worker@company.com manager@company.com /; my %services = ( service1 => { email => [ @maintainers ] }, # shallow copy of @main +tainers service2 => { email => [ @maintainers ] }, # another shallow ... ); print Data::Dumper->Dump( [ \%services ], [qw/*services/] ); exit 0; __DATA__ %services = ( 'service1' => { 'email' => [ 'worker@company.com', 'manager@company.com' ] }, 'service2' => { 'email' => [ 'worker@company.com', 'manager@company.com' ] } );

    One more thing. I was creating a shallow copy in the example above. For more complex data structures, you might need to copy recursively or use something like Storable::dclone or Clone.
Re: Putting an array into a hash gives an unexpected reference to the hash itself.
by kcott (Abbot) on Nov 07, 2012 at 23:34 UTC

    G'day polarbear,

    What you are describing is the default behaviour for Data::Dumper. You can change it by setting $Data::Dumper::Deepcopy:

    $ perl -Mstrict -Mwarnings -e ' use Data::Dumper; $Data::Dumper::Deepcopy = 1; my @maintainers = qw/ worker@company.com manager@company.com /; my %services = ( service1 => { email => \@maintainers }, service2 => { email => \@maintainers }, ); print Data::Dumper->Dump( [ \%services ], [qw/*services/] ); ' %services = ( 'service1' => { 'email' => [ 'worker@company.com', 'manager@company.com' ] }, 'service2' => { 'email' => [ 'worker@company.com', 'manager@company.com' ] } );

    Update: I may have misinterpreted "I don't want the lists connected in any way." as the cross-referencing of the lists output by Data::Dumper rather than the lists pointed to by the arrayrefs. If this is the case, the email => [ @maintainers ] solution would be the way to go. Thanks, frozenwithjoy.

    -- Ken

      Just a warning though that if you change one (e.g., push $services{"service1"}{"email"}, "xxx";), they will both change:
      %services = ( 'service1' => { 'email' => [ 'worker@company.com', 'manager@company.com', 'xxx' ] }, 'service2' => { 'email' => [ 'worker@company.com', 'manager@company.com', 'xxx' ] } );

        ++ Thanks for that. It looks like I misinterpreted the question. I've updated my response.

        -- Ken

Re: Putting an array into a hash gives an unexpected reference to the hash itself.
by AnomalousMonk (Monsignor) on Nov 08, 2012 at 03:05 UTC
    ... I am still puzzled as to why I got a reference to the hash value 'email' => $services{'service1'}{'email'} rather than a reference to say 'email' => $maintainers[0];

    The problem is that  Dump() does not 'know' that the reference that is the value of both  $services{service1}{email} and  $services{service2}{email} derives from the  @maintainers array, i.e.,  \@maintainers   (not the  $maintainers[0] array element). All  Dump() 'knows' is that one reference in the data structure it is analyzing (by recursive traversal of a tree of references!) is the same as another, and it tries to let you know (without quotation marks) this because it's probably significant. (Perl has a great deal of introspective ability, so it might be possible (but probably not – see Update below) to make a function like  Dump() smart enough figure out the name of the original referent (if it ever had and still has a name!), but if it is, the author of the module didn't know about the possibility or didn't care to make use of it.)

    Update: OTOH, it's easy to imagine situations in which the original name of a variable is gone beyond even the ability of PadWalker to access it. In the example below (don't try this at home: bug: dependence on execution order), the name of the lexical  @ra array is, I believe, absolutely gone upon exit from the scope in which it is defined, but the data lives on as long as there is a reference to it.

    >perl -wMstrict -le "use Data::Dumper; ;; { my @ra = qw/ worker@company.com manager@company.com /; sub AR { return \@ra; } } ;; my %services = ( service1 => { email => AR() }, service2 => { email => AR() }, ); ;; print Data::Dumper->Dump( [ \%services ], [qw/*services/] ); " %services = ( 'service1' => { 'email' => [ 'worker@company.com', 'manager@company.com' ] }, 'service2' => { 'email' => $services{'service1'}{'email' +} } );
      I doubt Data::Dumper inspects any variable names. It just uses what you give it - or VAR1, VAR2 if it gets nothing.
      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
        I doubt Data::Dumper inspects any variable names.

        It doesn't (as far as I know), and my code example would have been clearer had I not supplied a variable name to  Dump() in contrast to the OP. I just wanted to make the points that  Data::Dumper tries to emphasize repeated references within the data structure it's dumping (as these are usually important), and that even if one could sometimes recover the name of a reference, it probably wouldn't be worth doing.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1002765]
Approved by Perlbotics
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (8)
As of 2014-08-22 10:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (153 votes), past polls