copy "sub" hash?

Discussion in 'Perl Misc' started by catebekensail, May 27, 2014.

  1. how can you extract "mary" into another hash?

    %a = ('mary'=>
    {'address'=>11, 'zip'=>12},
    'john'=>
    {'address'=>21, 'zip'=>22},
    );

    %b = $a{'mary'}; ???
     
    catebekensail, May 27, 2014
    #1
    1. Advertisements

  2. catebekensail

    Steve May Guest

    You are copying a reference above.

    If you were to (attempt) to print out $b{'address'} you should see
    something like:

    my %a = ('mary'=>
    {'address'=>11, 'zip'=>12},
    'john'=>
    {'address'=>21, 'zip'=>22},
    );

    my %b = $a{'mary'};

    print qq~$b{'address'}\n~;

    Output ->
    Reference found where even-sized list expected
    Use of uninitialized value $b{"address"} in concatenation (.) or string


    Copying the dereferenced value of 'mary' instead:

    my %a = ('mary'=>
    {'address'=>11, 'zip'=>12},
    'john'=>
    {'address'=>21, 'zip'=>22},
    );

    my %b = %{$a{'mary'}};

    print qq~$b{'address'}\n~;

    Output -> 11

    The outer brackets are not mandatory, but I tend to use them in
    dereferencing as they are an immediate visual clue as to what the code
    is doing.

    You could also (simpler in my book) simply take a reference rather than
    copy into another hash if that works for your process....


    my %a = ('mary'=>
    {'address'=>11, 'zip'=>12},
    'john'=>
    {'address'=>21, 'zip'=>22},
    );

    my $mary = $a{'mary'};

    print $mary->{'address'};

    Output -> 11


    It might be worth your time to get a handle on references... You WILL
    need them sooner or later regardless and OOP is pretty much a mystery
    unless you understand them.

    hth,

    Steve
     
    Steve May, May 27, 2014
    #2
    1. Advertisements

  3. # ???!
    $b{mary} = $a{mary};
     
    George Mpouras, May 27, 2014
    #3
  4. Not really. Assigning a list to a hash causes the contents of the list
    to be turned into key/value pairs as they're encountered, eg this code

    [rw@sable]~#perl -e '%h = qw(a 1 b 2); print($h{a}, ", ", $h{b}, "\n")'
    1, 2

    causes %h to contain a key 'a' which is mapped to 1 and a key 'b' mapped
    to 2. $a{mary} returns the value associated with the key 'mary' in the
    hash %a. This is a reference to a hash. Since there's no other value,
    this is effectively a list of size 1. An attempt to use a reference as
    hash key will end up using the stringification of the reference instead,
    consequently, what

    %b = $a{mary}

    does is create a key a la 'HASH(0x8195c70)' in %b whose corresponding
    value is undef.

    [...]
    They are mandatory in this case:

    [rw@sable]~#perl -e '$h{a} = {h => a}; print %$h{a};'
    syntax error at -e line 1, near "$h{a"
    Execution of -e aborted due to compilation errors.

    The exact rules are (quoted from memory) that a simple, scalar variable
    holding a reference of the appropriate type can be used anywhere an
    identifier could appear as well. Further, a block returning a reference
    of an appropriate type can be used instead of the 'simple, scalar
    variable'. Since $h{a} is not a scalar variable, %$h{a} is a syntax
    error and the second form has to be used instead which is

    %{$h{a};}

    when written in full. Since the semicolon can be omitted after the last
    statement in a block, this can also be written as

    %{$h{a}}

    It is usually more convenient to use the ->-dereferencing operator
    instead of the 'block returning a reference' construct where possible,
    eg (showing all three ways to get the value associated with 'a' in the
    hash $h refers to):

    [rw@sable]~#perl -e '$h = { a => 3 }; print(join(", ", $$h{a}, ${$h}{a}, $h->{a}), "\n")'
    3, 3, 3
     
    Rainer Weikusat, May 27, 2014
    #4
  5. You need the key, too, not just the value:

    %b = (mary => $a{'mary'});

    (George's solution is equivalent if %b is empty. I leave it to you to
    figure out what the difference is if %b is not empty)

    hp
     
    Peter J. Holzer, May 27, 2014
    #5
  6. catebekensail

    Steve May Guest

    Well, that was directly copied from the error message generated when I
    ran the code shown and I would expect the same message to show up for
    the OP if they ran the same code. Which is why.... well you catch my drift.


    Yes, I was thinking about other cases, but in this case they are. I
    didn't catch that as I tend to use them all the time in dereferencing
    anyway.


    \s
     
    Steve May, May 27, 2014
    #6
  7. Except that, as I already wrote, the reference isn't copied: It is
    stringified and the result of that is used as a hash key (which will be
    mapped to undef because nothing except the reference was provided on the
    right-hand side of the assignment).

    [...]
    As I also already wrote: There's no relation between 'brackets' and
    'dereferencing': A block returning a reference of an appropriate type
    can be used instead of an ordinary identifier. That's a requirement in
    case such a reference isn't simply stored in a scalar variable and it
    isn't possible to use the 'infix dereference operator' (->)
    instead. The latter should usually be preferred, cf (other posting by me
    about this topic)

    https://groups.google.com/forum/#!original/comp.lang.perl.misc/sF52WmASiJ0/m8NXDrQqj5wJ
     
    Rainer Weikusat, May 27, 2014
    #7
  8. catebekensail

    Steve May Guest

    Sigh.... I believe we're saying much the same thing, just with different
    spins. You are probably more technically correct given that I'm just a
    shade tree hacker and that's fine.

    In my limited understanding the reference was not copied (correctly)
    since Perl does not implicitly dereference references. Given that all
    Perl arrays and hashes ONLY hold scalar values ( a string, number, or a
    reference), and the OP tried to assign that single internal scalar
    (reference pointer) to a hash, Perl said:

    'Reference found where even-sized list expected'

    Works for me.

    Whatever, but I think I'll continue to go with the approach shown in
    http://perldoc.perl.org/perldsc.html

    "If you want to get at the thing a reference is referring to, then you
    have to do this yourself using either prefix typing indicators, like
    ${$blah} , @{$blah} , @{$blah[$i]} , or else postfix pointer arrows,
    like $a->[3] , $h->{fred} , or even $ob->method()->[3] ."

    Again, works for me and I don't really care if there are technical
    quibbles. Life is too short.

    I DO appreciate your comments though, they make me at least think about
    the box though not necessarily outside of it. :)

    \s
     
    Steve May, May 27, 2014
    #8
  9. The usual term for copying what some 'reference' (or pointer) refers to,
    instead of copying the reference itself, would be 'deep copy' (as
    opposed to 'shallow copy') but that's yet something completely
    different.

    -----------
    my %hash_of_hopelessness = (
    anger => { address => 'hell', zip => 'Ha!'},
    despair => { address => 'heller', zip => 'Ha!ler'},
    ragnaroek =>{ address => 'gone', zip => '?' });


    my (%well_of_measles, %saubohne_des_verderbens);

    # associate a copy of the reference anger refers to with fear
    #
    $well_of_measles{fear} = $hash_of_hopelessness{anger};
    $hash_of_hopelessness{anger}{address} = 'damnation';

    print($well_of_measles{fear}{address}, "\n");

    # make a deep copy of the hash despair points to
    #
    $well_of_measles{trostlosigkeit} = {%{$hash_of_hopelessness{despair}}};
    $well_of_measles{trostlosigkeit}{address} = 'Asterdam';

    print($hash_of_hopelessness{despair}{address}, "\n");

    # copy the keys and values of the ragnaroek hash
    #
    %saubohne_des_verderbens = %{$hash_of_hopelessness{ragnaroek}};

    print("$_ -> $saubohne_des_verderbens{$_}\n") for keys(%saubohne_des_verderbens);

    # try to use a reference as hashkey
    #
    my %turn_of_the_stew;
    $turn_of_the_stew{{}} = {};

    # key isn't a reference anymore
    #
    printf("%s, %s(!)\n", $_, ref($_)) for keys(%turn_of_the_stew);
    --------------

    [...]
    I figure you believe to have found some contradiction between a text
    from the 'Perl data structures intro' tutorial and the perlref exceprt
    you're apparently incorrectly ascribing to me but at least insofar I'm
    concerned, I have no idea what this contradiction is suppse to be ...
     
    Rainer Weikusat, May 27, 2014
    #9
  10. FYI, in case you missed mention of "deep copy" in this thread:

    If you don't want sharing between the hashes, you'll need to clone
    rather than copy. If you just "copy" the reference ("shallow copy"),
    changes in one hash will be mirrored in the other.

    A simple example:

    %b = %a;
    $b{mary}{address} = 'foo'; ## $a{mary{address} changes too;


    Whereas, with a "deep copy":

    use Storable qw(dclone);

    my %b = %{ dclone(\%a) };
    $b{mary}{address}=12; ## $a{mary}{address} stays same


    See: perldoc -q recursive
     
    Charles DeRykus, May 28, 2014
    #10
  11. catebekensail

    Ted Zlatanov Guest

    On Mon, 26 May 2014 18:32:02 -0700 (PDT) wrote:

    c> how can you extract "mary" into another hash?
    c> %a = ('mary'=>
    c> {'address'=>11, 'zip'=>12},
    c> 'john'=>
    c> {'address'=>21, 'zip'=>22},
    c> );

    c> %b = $a{'mary'}; ???

    Nothing to add to the great answers so far, except that the newly
    released Perl 5.20.0 actually has this built-in, called a hash slice.
    For a friendly summary, see
    http://perltricks.com/article/92/2014/5/27/Perl-v5-20-what-you-need-to-know

    Ted
     
    Ted Zlatanov, May 28, 2014
    #11
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Similar Threads
There are no similar threads yet.
Loading...